mirror of
https://github.com/LukeHagar/vercel.git
synced 2025-12-11 21:07:47 +00:00
Compare commits
32 Commits
vercel-plu
...
@vercel/py
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5cf0c316e9 | ||
|
|
f4501433c8 | ||
|
|
19831593ce | ||
|
|
5d85bb1426 | ||
|
|
f194d54b0c | ||
|
|
6542086843 | ||
|
|
2721b3449d | ||
|
|
adb284519a | ||
|
|
b2d91f3121 | ||
|
|
32664cd13b | ||
|
|
db468c489a | ||
|
|
edd9bb506c | ||
|
|
a72549a290 | ||
|
|
4aa6a13912 | ||
|
|
81ea0082f1 | ||
|
|
6dff0875f5 | ||
|
|
30aa392c0a | ||
|
|
c4fc060030 | ||
|
|
3fa08bf64f | ||
|
|
43056bde1f | ||
|
|
a49966b9b4 | ||
|
|
7f55de71bb | ||
|
|
db8e36e04c | ||
|
|
82924bb5c4 | ||
|
|
18b5fac93e | ||
|
|
a6012e600b | ||
|
|
c3abf73f58 | ||
|
|
4873b8b379 | ||
|
|
6248139281 | ||
|
|
507a5de3cd | ||
|
|
be1c78e72f | ||
|
|
c277c649c6 |
2
.github/workflows/cancel.yml
vendored
2
.github/workflows/cancel.yml
vendored
@@ -11,7 +11,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 2
|
timeout-minutes: 2
|
||||||
steps:
|
steps:
|
||||||
- uses: styfle/cancel-workflow-action@0.4.1
|
- uses: styfle/cancel-workflow-action@0.9.1
|
||||||
with:
|
with:
|
||||||
workflow_id: 849295, 849296, 849297, 849298
|
workflow_id: 849295, 849296, 849297, 849298
|
||||||
access_token: ${{ github.token }}
|
access_token: ${{ github.token }}
|
||||||
|
|||||||
2
.github/workflows/test-integration-dev.yml
vendored
2
.github/workflows/test-integration-dev.yml
vendored
@@ -11,7 +11,7 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
test:
|
test:
|
||||||
name: Dev
|
name: Dev
|
||||||
timeout-minutes: 60
|
timeout-minutes: 75
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -27,3 +27,4 @@ test/lib/deployment/failed-page.txt
|
|||||||
/public
|
/public
|
||||||
__pycache__
|
__pycache__
|
||||||
.vercel
|
.vercel
|
||||||
|
.output
|
||||||
|
|||||||
@@ -332,7 +332,7 @@ This is an abstract enumeration type that is implemented by one of the following
|
|||||||
- `nodejs10.x`
|
- `nodejs10.x`
|
||||||
- `go1.x`
|
- `go1.x`
|
||||||
- `java11`
|
- `java11`
|
||||||
- `python3.8`
|
- `python3.9`
|
||||||
- `python3.6`
|
- `python3.6`
|
||||||
- `dotnetcore2.1`
|
- `dotnetcore2.1`
|
||||||
- `ruby2.5`
|
- `ruby2.5`
|
||||||
@@ -398,12 +398,12 @@ This utility allows you to _scan_ the filesystem and return a [`Files`](#files)
|
|||||||
The following trivial example downloads everything to the filesystem, only to return it back (therefore just re-creating the passed-in [`Files`](#files)):
|
The following trivial example downloads everything to the filesystem, only to return it back (therefore just re-creating the passed-in [`Files`](#files)):
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const { glob, download } = require('@vercel/build-utils')
|
const { glob, download } = require('@vercel/build-utils');
|
||||||
|
|
||||||
exports.build = ({ files, workPath }) => {
|
exports.build = ({ files, workPath }) => {
|
||||||
await download(files, workPath)
|
await download(files, workPath);
|
||||||
return glob('**', workPath)
|
return glob('**', workPath);
|
||||||
}
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
### `getWritableDirectory()`
|
### `getWritableDirectory()`
|
||||||
|
|||||||
@@ -14,8 +14,6 @@ const frameworks = (_frameworks as Framework[])
|
|||||||
sort: undefined,
|
sort: undefined,
|
||||||
dependency: undefined,
|
dependency: undefined,
|
||||||
defaultRoutes: undefined,
|
defaultRoutes: undefined,
|
||||||
devCommand: undefined,
|
|
||||||
buildCommand: undefined,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (framework.logo) {
|
if (framework.logo) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vercel/build-utils",
|
"name": "@vercel/build-utils",
|
||||||
"version": "2.12.3-canary.17",
|
"version": "2.12.3-canary.20",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"types": "./dist/index.d.js",
|
"types": "./dist/index.d.js",
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
"@types/node-fetch": "^2.1.6",
|
"@types/node-fetch": "^2.1.6",
|
||||||
"@types/semver": "6.0.0",
|
"@types/semver": "6.0.0",
|
||||||
"@types/yazl": "^2.4.1",
|
"@types/yazl": "^2.4.1",
|
||||||
"@vercel/frameworks": "0.5.1-canary.11",
|
"@vercel/frameworks": "0.5.1-canary.12",
|
||||||
"@vercel/ncc": "0.24.0",
|
"@vercel/ncc": "0.24.0",
|
||||||
"aggregate-error": "3.0.1",
|
"aggregate-error": "3.0.1",
|
||||||
"async-retry": "1.2.3",
|
"async-retry": "1.2.3",
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import fs from 'fs-extra';
|
|||||||
import { join, dirname, relative } from 'path';
|
import { join, dirname, relative } from 'path';
|
||||||
import glob from './fs/glob';
|
import glob from './fs/glob';
|
||||||
import { normalizePath } from './fs/normalize-path';
|
import { normalizePath } from './fs/normalize-path';
|
||||||
import { FILES_SYMBOL, Lambda } from './lambda';
|
import { FILES_SYMBOL, getLambdaOptionsFromFunction, Lambda } from './lambda';
|
||||||
import type FileBlob from './file-blob';
|
import type FileBlob from './file-blob';
|
||||||
import type { BuilderFunctions, BuildOptions, Files } from './types';
|
import type { BuilderFunctions, BuildOptions, Files } from './types';
|
||||||
import minimatch from 'minimatch';
|
import minimatch from 'minimatch';
|
||||||
@@ -21,8 +21,8 @@ export function convertRuntimeToPlugin(
|
|||||||
const files = await glob('**', opts);
|
const files = await glob('**', opts);
|
||||||
delete files['vercel.json']; // Builders/Runtimes didn't have vercel.json
|
delete files['vercel.json']; // Builders/Runtimes didn't have vercel.json
|
||||||
const entrypoints = await glob(`api/**/*${ext}`, opts);
|
const entrypoints = await glob(`api/**/*${ext}`, opts);
|
||||||
const functionsManifest: { [key: string]: any } = {};
|
const pages: { [key: string]: any } = {};
|
||||||
const functions = await readVercelConfigFunctions(workPath);
|
const { functions = {} } = await readVercelConfig(workPath);
|
||||||
const traceDir = join(workPath, '.output', 'runtime-traced-files');
|
const traceDir = join(workPath, '.output', 'runtime-traced-files');
|
||||||
await fs.ensureDir(traceDir);
|
await fs.ensureDir(traceDir);
|
||||||
|
|
||||||
@@ -44,11 +44,11 @@ export function convertRuntimeToPlugin(
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
functionsManifest[entrypoint] = {
|
pages[entrypoint] = {
|
||||||
handler: output.handler,
|
handler: output.handler,
|
||||||
runtime: output.runtime,
|
runtime: output.runtime,
|
||||||
memory: config.memory || output.memory,
|
memory: output.memory,
|
||||||
maxDuration: config.maxDuration || output.maxDuration,
|
maxDuration: output.maxDuration,
|
||||||
environment: output.environment,
|
environment: output.environment,
|
||||||
allowQuery: output.allowQuery,
|
allowQuery: output.allowQuery,
|
||||||
regions: output.regions,
|
regions: output.regions,
|
||||||
@@ -98,10 +98,7 @@ export function convertRuntimeToPlugin(
|
|||||||
await fs.writeFile(nft, json);
|
await fs.writeFile(nft, json);
|
||||||
}
|
}
|
||||||
|
|
||||||
await fs.writeFile(
|
await updateFunctionsManifest({ workPath, pages });
|
||||||
join(workPath, '.output', 'functions-manifest.json'),
|
|
||||||
JSON.stringify(functionsManifest)
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,14 +112,10 @@ async function linkOrCopy(existingPath: string, newPath: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function readVercelConfigFunctions(
|
async function readJson(filePath: string): Promise<{ [key: string]: any }> {
|
||||||
workPath: string
|
|
||||||
): Promise<BuilderFunctions> {
|
|
||||||
const vercelJsonPath = join(workPath, 'vercel.json');
|
|
||||||
try {
|
try {
|
||||||
const str = await fs.readFile(vercelJsonPath, 'utf8');
|
const str = await fs.readFile(filePath, 'utf8');
|
||||||
const obj = JSON.parse(str);
|
return JSON.parse(str);
|
||||||
return obj.functions || {};
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err.code === 'ENOENT') {
|
if (err.code === 'ENOENT') {
|
||||||
return {};
|
return {};
|
||||||
@@ -130,3 +123,80 @@ async function readVercelConfigFunctions(
|
|||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function readVercelConfig(
|
||||||
|
workPath: string
|
||||||
|
): Promise<{ functions?: BuilderFunctions; regions?: string[] }> {
|
||||||
|
const vercelJsonPath = join(workPath, 'vercel.json');
|
||||||
|
return readJson(vercelJsonPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If `.output/functions-manifest.json` exists, append to the pages
|
||||||
|
* property. Otherwise write a new file. This will also read `vercel.json`
|
||||||
|
* and apply relevant `functions` property config.
|
||||||
|
*/
|
||||||
|
export async function updateFunctionsManifest({
|
||||||
|
workPath,
|
||||||
|
pages,
|
||||||
|
}: {
|
||||||
|
workPath: string;
|
||||||
|
pages: { [key: string]: any };
|
||||||
|
}) {
|
||||||
|
const functionsManifestPath = join(
|
||||||
|
workPath,
|
||||||
|
'.output',
|
||||||
|
'functions-manifest.json'
|
||||||
|
);
|
||||||
|
const vercelConfig = await readVercelConfig(workPath);
|
||||||
|
const functionsManifest = await readJson(functionsManifestPath);
|
||||||
|
|
||||||
|
if (!functionsManifest.version) functionsManifest.version = 1;
|
||||||
|
if (!functionsManifest.pages) functionsManifest.pages = {};
|
||||||
|
|
||||||
|
for (const [pageKey, pageConfig] of Object.entries(pages)) {
|
||||||
|
const fnConfig = await getLambdaOptionsFromFunction({
|
||||||
|
sourceFile: pageKey,
|
||||||
|
config: vercelConfig,
|
||||||
|
});
|
||||||
|
functionsManifest.pages[pageKey] = {
|
||||||
|
...pageConfig,
|
||||||
|
memory: fnConfig.memory || pageConfig.memory,
|
||||||
|
maxDuration: fnConfig.maxDuration || pageConfig.maxDuration,
|
||||||
|
regions: vercelConfig.regions || pageConfig.regions,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
await fs.writeFile(functionsManifestPath, JSON.stringify(functionsManifest));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will append routes to the `routes-manifest.json` file.
|
||||||
|
* If the file does not exist, it'll be created.
|
||||||
|
*/
|
||||||
|
export async function updateRoutesManifest({
|
||||||
|
workPath,
|
||||||
|
dynamicRoutes,
|
||||||
|
}: {
|
||||||
|
workPath: string;
|
||||||
|
dynamicRoutes?: {
|
||||||
|
page: string;
|
||||||
|
regex: string;
|
||||||
|
namedRegex?: string;
|
||||||
|
routeKeys?: { [named: string]: string };
|
||||||
|
}[];
|
||||||
|
}) {
|
||||||
|
const routesManifestPath = join(workPath, '.output', 'routes-manifest.json');
|
||||||
|
|
||||||
|
const routesManifest = await readJson(routesManifestPath);
|
||||||
|
|
||||||
|
if (!routesManifest.version) routesManifest.version = 1;
|
||||||
|
if (routesManifest.pages404 === undefined) routesManifest.pages404 = true;
|
||||||
|
|
||||||
|
if (dynamicRoutes) {
|
||||||
|
if (!routesManifest.dynamicRoutes) routesManifest.dynamicRoutes = [];
|
||||||
|
routesManifest.dynamicRoutes.push(...dynamicRoutes);
|
||||||
|
}
|
||||||
|
|
||||||
|
await fs.writeFile(routesManifestPath, JSON.stringify(routesManifest));
|
||||||
|
}
|
||||||
|
|||||||
@@ -82,7 +82,11 @@ export { detectFramework } from './detect-framework';
|
|||||||
export { DetectorFilesystem } from './detectors/filesystem';
|
export { DetectorFilesystem } from './detectors/filesystem';
|
||||||
export { readConfigFile } from './fs/read-config-file';
|
export { readConfigFile } from './fs/read-config-file';
|
||||||
export { normalizePath } from './fs/normalize-path';
|
export { normalizePath } from './fs/normalize-path';
|
||||||
export { convertRuntimeToPlugin } from './convert-runtime-to-plugin';
|
export {
|
||||||
|
convertRuntimeToPlugin,
|
||||||
|
updateFunctionsManifest,
|
||||||
|
updateRoutesManifest,
|
||||||
|
} from './convert-runtime-to-plugin';
|
||||||
|
|
||||||
export * from './schemas';
|
export * from './schemas';
|
||||||
export * from './types';
|
export * from './types';
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ interface CreateLambdaOptions {
|
|||||||
|
|
||||||
interface GetLambdaOptionsFromFunctionOptions {
|
interface GetLambdaOptionsFromFunctionOptions {
|
||||||
sourceFile: string;
|
sourceFile: string;
|
||||||
config?: Config;
|
config?: Pick<Config, 'functions'>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const FILES_SYMBOL = Symbol('files');
|
export const FILES_SYMBOL = Symbol('files');
|
||||||
|
|||||||
@@ -71,9 +71,12 @@ describe('convert-runtime-to-plugin', () => {
|
|||||||
|
|
||||||
const funcManifest = JSON.parse(output['functions-manifest.json']);
|
const funcManifest = JSON.parse(output['functions-manifest.json']);
|
||||||
expect(funcManifest).toMatchObject({
|
expect(funcManifest).toMatchObject({
|
||||||
'api/index.py': lambdaOptions,
|
version: 1,
|
||||||
'api/users/get.py': lambdaOptions,
|
pages: {
|
||||||
'api/users/post.py': { ...lambdaOptions, memory: 3008 },
|
'api/index.py': lambdaOptions,
|
||||||
|
'api/users/get.py': lambdaOptions,
|
||||||
|
'api/users/post.py': { ...lambdaOptions, memory: 3008 },
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const indexJson = JSON.parse(output.server.pages.api['index.py.nft.json']);
|
const indexJson = JSON.parse(output.server.pages.api['index.py.nft.json']);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "vercel",
|
"name": "vercel",
|
||||||
"version": "23.1.3-canary.28",
|
"version": "23.1.3-canary.38",
|
||||||
"preferGlobal": true,
|
"preferGlobal": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"description": "The command-line interface for Vercel",
|
"description": "The command-line interface for Vercel",
|
||||||
@@ -43,14 +43,14 @@
|
|||||||
"node": ">= 12"
|
"node": ">= 12"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vercel/build-utils": "2.12.3-canary.17",
|
"@vercel/build-utils": "2.12.3-canary.20",
|
||||||
"@vercel/go": "1.2.4-canary.3",
|
"@vercel/go": "1.2.4-canary.4",
|
||||||
"@vercel/node": "1.12.2-canary.6",
|
"@vercel/node": "1.12.2-canary.7",
|
||||||
"@vercel/python": "2.0.6-canary.5",
|
"@vercel/python": "2.1.0",
|
||||||
"@vercel/ruby": "1.2.8-canary.4",
|
"@vercel/ruby": "1.2.8-canary.4",
|
||||||
"update-notifier": "4.1.0",
|
"update-notifier": "4.1.0",
|
||||||
"vercel-plugin-middleware": "0.0.0-canary.6",
|
"vercel-plugin-middleware": "0.0.0-canary.7",
|
||||||
"vercel-plugin-node": "1.12.2-canary.7"
|
"vercel-plugin-node": "1.12.2-canary.11"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@next/env": "11.1.2",
|
"@next/env": "11.1.2",
|
||||||
@@ -90,7 +90,7 @@
|
|||||||
"@types/update-notifier": "5.1.0",
|
"@types/update-notifier": "5.1.0",
|
||||||
"@types/which": "1.3.2",
|
"@types/which": "1.3.2",
|
||||||
"@types/write-json-file": "2.2.1",
|
"@types/write-json-file": "2.2.1",
|
||||||
"@vercel/frameworks": "0.5.1-canary.11",
|
"@vercel/frameworks": "0.5.1-canary.12",
|
||||||
"@vercel/ncc": "0.24.0",
|
"@vercel/ncc": "0.24.0",
|
||||||
"@vercel/nft": "0.17.0",
|
"@vercel/nft": "0.17.0",
|
||||||
"@zeit/fun": "0.11.2",
|
"@zeit/fun": "0.11.2",
|
||||||
|
|||||||
@@ -30,10 +30,7 @@ import { getCommandName, getPkgName } from '../util/pkg-name';
|
|||||||
import { loadCliPlugins } from '../util/plugins';
|
import { loadCliPlugins } from '../util/plugins';
|
||||||
import { findFramework } from '../util/projects/find-framework';
|
import { findFramework } from '../util/projects/find-framework';
|
||||||
import { VERCEL_DIR } from '../util/projects/link';
|
import { VERCEL_DIR } from '../util/projects/link';
|
||||||
import {
|
import { readProjectSettings } from '../util/projects/project-settings';
|
||||||
ProjectLinkAndSettings,
|
|
||||||
readProjectSettings,
|
|
||||||
} from '../util/projects/project-settings';
|
|
||||||
import pull from './pull';
|
import pull from './pull';
|
||||||
|
|
||||||
const sema = new Sema(16, {
|
const sema = new Sema(16, {
|
||||||
@@ -68,16 +65,23 @@ const help = () => {
|
|||||||
|
|
||||||
const OUTPUT_DIR = '.output';
|
const OUTPUT_DIR = '.output';
|
||||||
|
|
||||||
const fields: {
|
|
||||||
name: string;
|
|
||||||
value: keyof ProjectLinkAndSettings['settings'];
|
|
||||||
}[] = [
|
|
||||||
{ name: 'Build Command', value: 'buildCommand' },
|
|
||||||
{ name: 'Output Directory', value: 'outputDirectory' },
|
|
||||||
{ name: 'Root Directory', value: 'rootDirectory' },
|
|
||||||
];
|
|
||||||
|
|
||||||
export default async function main(client: Client) {
|
export default async function main(client: Client) {
|
||||||
|
if (process.env.__VERCEL_BUILD_RUNNING) {
|
||||||
|
client.output.error(
|
||||||
|
`${cmd(
|
||||||
|
`${getPkgName()} build`
|
||||||
|
)} must not recursively invoke itself. Check the Build Command in the Project Settings or the ${cmd(
|
||||||
|
'build'
|
||||||
|
)} script in ${cmd('package.json')}`
|
||||||
|
);
|
||||||
|
client.output.error(
|
||||||
|
`Learn More: https://vercel.link/recursive-invocation-of-commands`
|
||||||
|
);
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
process.env.__VERCEL_BUILD_RUNNING = '1';
|
||||||
|
}
|
||||||
|
|
||||||
let argv;
|
let argv;
|
||||||
const buildStamp = stamp();
|
const buildStamp = stamp();
|
||||||
try {
|
try {
|
||||||
@@ -152,47 +156,57 @@ export default async function main(client: Client) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const buildState = { ...project.settings };
|
const buildState = { ...project.settings };
|
||||||
|
const formatSetting = (
|
||||||
client.output.log(`Retrieved Project Settings:`);
|
name: string,
|
||||||
client.output.print(
|
override: string | null | undefined,
|
||||||
chalk.dim(` - ${chalk.bold(`Framework Preset:`)} ${framework.name}\n`)
|
defaults: typeof framework.settings.outputDirectory
|
||||||
|
) =>
|
||||||
|
` - ${chalk.bold(`${name}:`)} ${`${
|
||||||
|
override
|
||||||
|
? override + ` (override)`
|
||||||
|
: 'placeholder' in defaults
|
||||||
|
? chalk.italic(`${defaults.placeholder}`)
|
||||||
|
: defaults.value
|
||||||
|
}`}`;
|
||||||
|
console.log(`Retrieved Project Settings:`);
|
||||||
|
console.log(
|
||||||
|
chalk.dim(` - ${chalk.bold(`Framework Preset:`)} ${framework.name}`)
|
||||||
|
);
|
||||||
|
console.log(
|
||||||
|
chalk.dim(
|
||||||
|
formatSetting(
|
||||||
|
'Build Command',
|
||||||
|
project.settings.buildCommand,
|
||||||
|
framework.settings.buildCommand
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
console.log(
|
||||||
|
chalk.dim(
|
||||||
|
formatSetting(
|
||||||
|
'Output Directory',
|
||||||
|
project.settings.outputDirectory,
|
||||||
|
framework.settings.outputDirectory
|
||||||
|
)
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
for (let field of fields) {
|
buildState.outputDirectory =
|
||||||
const defaults = (framework.settings as any)[field.value];
|
project.settings.outputDirectory ||
|
||||||
if (defaults) {
|
(isSettingValue(framework.settings.outputDirectory)
|
||||||
client.output.print(
|
? framework.settings.outputDirectory.value
|
||||||
chalk.dim(
|
: null);
|
||||||
` - ${chalk.bold(`${field.name}:`)} ${`${
|
buildState.rootDirectory = project.settings.rootDirectory;
|
||||||
project.settings[field.value]
|
|
||||||
? project.settings[field.value] + ` (override)`
|
|
||||||
: isSettingValue(defaults)
|
|
||||||
? defaults.value
|
|
||||||
: chalk.italic(`${defaults.placeholder}`)
|
|
||||||
}`}\n`
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (field.value != 'buildCommand') {
|
|
||||||
(buildState as any)[field.value] = project.settings[field.value]
|
|
||||||
? project.settings[field.value]
|
|
||||||
: defaults
|
|
||||||
? isSettingValue(defaults)
|
|
||||||
? defaults.value
|
|
||||||
: null
|
|
||||||
: null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (loadedEnvFiles.length > 0) {
|
if (loadedEnvFiles.length > 0) {
|
||||||
client.output.log(
|
console.log(
|
||||||
`Loaded Environment Variables from ${loadedEnvFiles.length} ${pluralize(
|
`Loaded Environment Variables from ${loadedEnvFiles.length} ${pluralize(
|
||||||
'file',
|
'file',
|
||||||
loadedEnvFiles.length
|
loadedEnvFiles.length
|
||||||
)}:`
|
)}:`
|
||||||
);
|
);
|
||||||
for (let envFile of loadedEnvFiles) {
|
for (let envFile of loadedEnvFiles) {
|
||||||
client.output.print(chalk.dim(` - ${envFile.path}\n`));
|
console.log(chalk.dim(` - ${envFile.path}`));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,7 +237,7 @@ export default async function main(client: Client) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (plugins?.pluginCount && plugins?.pluginCount > 0) {
|
if (plugins?.pluginCount && plugins?.pluginCount > 0) {
|
||||||
client.output.log(
|
console.log(
|
||||||
`Loaded ${plugins.pluginCount} CLI ${pluralize(
|
`Loaded ${plugins.pluginCount} CLI ${pluralize(
|
||||||
'Plugin',
|
'Plugin',
|
||||||
plugins.pluginCount
|
plugins.pluginCount
|
||||||
@@ -231,7 +245,7 @@ export default async function main(client: Client) {
|
|||||||
);
|
);
|
||||||
// preBuild Plugins
|
// preBuild Plugins
|
||||||
if (plugins.preBuildPlugins.length > 0) {
|
if (plugins.preBuildPlugins.length > 0) {
|
||||||
client.output.log(
|
console.log(
|
||||||
`Running ${plugins.pluginCount} CLI ${pluralize(
|
`Running ${plugins.pluginCount} CLI ${pluralize(
|
||||||
'Plugin',
|
'Plugin',
|
||||||
plugins.pluginCount
|
plugins.pluginCount
|
||||||
@@ -269,17 +283,19 @@ export default async function main(client: Client) {
|
|||||||
// Clean the output directory
|
// Clean the output directory
|
||||||
fs.removeSync(join(cwd, OUTPUT_DIR));
|
fs.removeSync(join(cwd, OUTPUT_DIR));
|
||||||
|
|
||||||
|
// Yarn v2 PnP mode may be activated, so force
|
||||||
|
// "node-modules" linker style
|
||||||
|
const env = {
|
||||||
|
YARN_NODE_LINKER: 'node-modules',
|
||||||
|
...spawnOpts.env,
|
||||||
|
};
|
||||||
|
|
||||||
if (typeof buildState.buildCommand === 'string') {
|
if (typeof buildState.buildCommand === 'string') {
|
||||||
client.output.log(`Running Build Command: ${cmd(buildState.buildCommand)}`);
|
console.log(`Running Build Command: ${cmd(buildState.buildCommand)}`);
|
||||||
await execCommand(buildState.buildCommand, {
|
await execCommand(buildState.buildCommand, {
|
||||||
...spawnOpts,
|
...spawnOpts,
|
||||||
// Yarn v2 PnP mode may be activated, so force
|
env,
|
||||||
// "node-modules" linker style
|
cwd,
|
||||||
env: {
|
|
||||||
YARN_NODE_LINKER: 'node-modules',
|
|
||||||
...spawnOpts.env,
|
|
||||||
},
|
|
||||||
cwd: cwd,
|
|
||||||
});
|
});
|
||||||
} else if (fs.existsSync(join(cwd, 'package.json'))) {
|
} else if (fs.existsSync(join(cwd, 'package.json'))) {
|
||||||
await runPackageJsonScript(
|
await runPackageJsonScript(
|
||||||
@@ -288,6 +304,15 @@ export default async function main(client: Client) {
|
|||||||
['vercel-build', 'now-build', 'build'],
|
['vercel-build', 'now-build', 'build'],
|
||||||
spawnOpts
|
spawnOpts
|
||||||
);
|
);
|
||||||
|
} else if (typeof framework.settings.buildCommand.value === 'string') {
|
||||||
|
console.log(
|
||||||
|
`Running Build Command: ${cmd(framework.settings.buildCommand.value)}`
|
||||||
|
);
|
||||||
|
await execCommand(framework.settings.buildCommand.value, {
|
||||||
|
...spawnOpts,
|
||||||
|
env,
|
||||||
|
cwd,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!fs.existsSync(join(cwd, OUTPUT_DIR))) {
|
if (!fs.existsSync(join(cwd, OUTPUT_DIR))) {
|
||||||
@@ -336,7 +361,7 @@ export default async function main(client: Client) {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
client.output.stopSpinner();
|
client.output.stopSpinner();
|
||||||
client.output.log(
|
console.log(
|
||||||
`Copied ${files.length.toLocaleString()} files from ${param(
|
`Copied ${files.length.toLocaleString()} files from ${param(
|
||||||
distDir
|
distDir
|
||||||
)} to ${param(outputDir)} ${copyStamp()}`
|
)} to ${param(outputDir)} ${copyStamp()}`
|
||||||
@@ -557,7 +582,7 @@ export default async function main(client: Client) {
|
|||||||
|
|
||||||
// Build Plugins
|
// Build Plugins
|
||||||
if (plugins?.buildPlugins && plugins.buildPlugins.length > 0) {
|
if (plugins?.buildPlugins && plugins.buildPlugins.length > 0) {
|
||||||
client.output.log(
|
console.log(
|
||||||
`Running ${plugins.pluginCount} CLI ${pluralize(
|
`Running ${plugins.pluginCount} CLI ${pluralize(
|
||||||
'Plugin',
|
'Plugin',
|
||||||
plugins.pluginCount
|
plugins.pluginCount
|
||||||
@@ -592,13 +617,13 @@ export default async function main(client: Client) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
client.output.print(
|
console.log(
|
||||||
`${prependEmoji(
|
`${prependEmoji(
|
||||||
`Build Completed in ${chalk.bold(OUTPUT_DIR)} ${chalk.gray(
|
`Build Completed in ${chalk.bold(OUTPUT_DIR)} ${chalk.gray(
|
||||||
buildStamp()
|
buildStamp()
|
||||||
)}`,
|
)}`,
|
||||||
emoji('success')
|
emoji('success')
|
||||||
)}\n`
|
)}`
|
||||||
);
|
);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -644,25 +669,37 @@ export async function runPackageJsonScript(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
client.output.log(`Running Build Command: ${cmd(opts.prettyCommand)}\n`);
|
console.log(`Running Build Command: ${cmd(opts.prettyCommand)}\n`);
|
||||||
await spawnAsync(cliType, ['run', scriptName], opts);
|
await spawnAsync(cliType, ['run', scriptName], opts);
|
||||||
client.output.print('\n'); // give it some room
|
console.log(); // give it some room
|
||||||
client.output.debug(`Script complete [${Date.now() - runScriptTime}ms]`);
|
client.output.debug(`Script complete [${Date.now() - runScriptTime}ms]`);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function linkOrCopy(existingPath: string, newPath: string) {
|
async function linkOrCopy(existingPath: string, newPath: string) {
|
||||||
try {
|
try {
|
||||||
await fs.createLink(existingPath, newPath);
|
if (
|
||||||
|
newPath.endsWith('.nft.json') ||
|
||||||
|
newPath.endsWith('middleware-manifest.json') ||
|
||||||
|
newPath.endsWith('required-server-files.json')
|
||||||
|
) {
|
||||||
|
await fs.copy(existingPath, newPath, {
|
||||||
|
overwrite: true,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await fs.createLink(existingPath, newPath);
|
||||||
|
}
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
// eslint-disable-line
|
// eslint-disable-line
|
||||||
// If a hard link to the same file already exists
|
// If a symlink to the same file already exists
|
||||||
// then trying to copy it will make an empty file from it.
|
// then trying to copy it will make an empty file from it.
|
||||||
if (err['code'] === 'EEXIST') return;
|
if (err['code'] === 'EEXIST') return;
|
||||||
// In some VERY rare cases (1 in a thousand), hard-link creation fails on Windows.
|
// In some VERY rare cases (1 in a thousand), symlink creation fails on Windows.
|
||||||
// In that case, we just fall back to copying.
|
// In that case, we just fall back to copying.
|
||||||
// This issue is reproducible with "pnpm add @material-ui/icons@4.9.1"
|
// This issue is reproducible with "pnpm add @material-ui/icons@4.9.1"
|
||||||
await fs.copyFile(existingPath, newPath);
|
await fs.copy(existingPath, newPath, {
|
||||||
|
overwrite: true,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import { ProjectEnvVariable } from '../../types';
|
|||||||
import Client from '../../util/client';
|
import Client from '../../util/client';
|
||||||
import { getLinkedProject } from '../../util/projects/link';
|
import { getLinkedProject } from '../../util/projects/link';
|
||||||
import { getFrameworks } from '../../util/get-frameworks';
|
import { getFrameworks } from '../../util/get-frameworks';
|
||||||
import { isSettingValue } from '../../util/is-setting-value';
|
|
||||||
import { ProjectSettings } from '../../types';
|
import { ProjectSettings } from '../../types';
|
||||||
import getDecryptedEnvRecords from '../../util/get-decrypted-env-records';
|
import getDecryptedEnvRecords from '../../util/get-decrypted-env-records';
|
||||||
import setupAndLink from '../../util/link/setup-and-link';
|
import setupAndLink from '../../util/link/setup-and-link';
|
||||||
@@ -71,9 +70,9 @@ export default async function dev(
|
|||||||
frameworkSlug = framework.slug;
|
frameworkSlug = framework.slug;
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaults = framework.settings.devCommand;
|
const defaults = framework.settings.devCommand.value;
|
||||||
if (isSettingValue(defaults)) {
|
if (defaults) {
|
||||||
devCommand = defaults.value;
|
devCommand = defaults;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,6 +48,22 @@ const help = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default async function main(client: Client) {
|
export default async function main(client: Client) {
|
||||||
|
if (process.env.__VERCEL_DEV_RUNNING) {
|
||||||
|
client.output.error(
|
||||||
|
`${cmd(
|
||||||
|
`${getPkgName()} dev`
|
||||||
|
)} must not recursively invoke itself. Check the Development Command in the Project Settings or the ${cmd(
|
||||||
|
'dev'
|
||||||
|
)} script in ${cmd('package.json')}`
|
||||||
|
);
|
||||||
|
client.output.error(
|
||||||
|
`Learn More: https://vercel.link/recursive-invocation-of-commands`
|
||||||
|
);
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
process.env.__VERCEL_DEV_RUNNING = '1';
|
||||||
|
}
|
||||||
|
|
||||||
let argv;
|
let argv;
|
||||||
let args;
|
let args;
|
||||||
const { output } = client;
|
const { output } = client;
|
||||||
@@ -90,22 +106,21 @@ export default async function main(client: Client) {
|
|||||||
if (pkg) {
|
if (pkg) {
|
||||||
const { scripts } = pkg as PackageJson;
|
const { scripts } = pkg as PackageJson;
|
||||||
|
|
||||||
if (scripts && scripts.dev && /\bnow\b\W+\bdev\b/.test(scripts.dev)) {
|
if (
|
||||||
output.error(
|
scripts &&
|
||||||
`The ${cmd('dev')} script in ${cmd(
|
scripts.dev &&
|
||||||
'package.json'
|
/\b(now|vercel)\b\W+\bdev\b/.test(scripts.dev)
|
||||||
)} must not contain ${cmd('now dev')}`
|
) {
|
||||||
|
client.output.error(
|
||||||
|
`${cmd(
|
||||||
|
`${getPkgName()} dev`
|
||||||
|
)} must not recursively invoke itself. Check the Development Command in the Project Settings or the ${cmd(
|
||||||
|
'dev'
|
||||||
|
)} script in ${cmd('package.json')}`
|
||||||
);
|
);
|
||||||
output.error(`Learn More: http://err.sh/vercel/now-dev-as-dev-script`);
|
client.output.error(
|
||||||
return 1;
|
`Learn More: https://vercel.link/recursive-invocation-of-commands`
|
||||||
}
|
|
||||||
if (scripts && scripts.dev && /\bvercel\b\W+\bdev\b/.test(scripts.dev)) {
|
|
||||||
output.error(
|
|
||||||
`The ${cmd('dev')} script in ${cmd(
|
|
||||||
'package.json'
|
|
||||||
)} must not contain ${cmd('vercel dev')}`
|
|
||||||
);
|
);
|
||||||
output.error(`Learn More: http://err.sh/vercel/now-dev-as-dev-script`);
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -160,24 +160,26 @@ const main = async () => {
|
|||||||
// * a path to deploy (as in: `vercel path/`)
|
// * a path to deploy (as in: `vercel path/`)
|
||||||
// * a subcommand (as in: `vercel ls`)
|
// * a subcommand (as in: `vercel ls`)
|
||||||
const targetOrSubcommand = argv._[2];
|
const targetOrSubcommand = argv._[2];
|
||||||
|
const isBuildOrDev =
|
||||||
|
targetOrSubcommand === 'build' || targetOrSubcommand === 'dev';
|
||||||
|
|
||||||
output.print(
|
if (isBuildOrDev) {
|
||||||
`${chalk.grey(
|
console.log(
|
||||||
`${getTitleName()} CLI ${pkg.version}${
|
`${chalk.grey(
|
||||||
targetOrSubcommand === 'dev'
|
`${getTitleName()} CLI ${
|
||||||
? ' dev (beta)'
|
pkg.version
|
||||||
: targetOrSubcommand === 'build'
|
} ${targetOrSubcommand} (beta) — https://vercel.com/feedback`
|
||||||
? ' build (beta)'
|
)}`
|
||||||
: ''
|
);
|
||||||
}${
|
} else {
|
||||||
isCanary ||
|
output.print(
|
||||||
targetOrSubcommand === 'dev' ||
|
`${chalk.grey(
|
||||||
targetOrSubcommand === 'build'
|
`${getTitleName()} CLI ${pkg.version}${
|
||||||
? ' — https://vercel.com/feedback'
|
isCanary ? ' — https://vercel.com/feedback' : ''
|
||||||
: ''
|
}`
|
||||||
}`
|
)}\n`
|
||||||
)}\n`
|
);
|
||||||
);
|
}
|
||||||
|
|
||||||
// Handle `--version` directly
|
// Handle `--version` directly
|
||||||
if (!targetOrSubcommand && argv['--version']) {
|
if (!targetOrSubcommand && argv['--version']) {
|
||||||
|
|||||||
@@ -18,12 +18,8 @@ export const isDirectory = (path: string): boolean => {
|
|||||||
const getGlobalPathConfig = (): string => {
|
const getGlobalPathConfig = (): string => {
|
||||||
let customPath: string | undefined;
|
let customPath: string | undefined;
|
||||||
|
|
||||||
try {
|
const argv = getArgs(process.argv.slice(2), {}, { permissive: true });
|
||||||
const argv = getArgs(process.argv.slice(2), {});
|
customPath = argv['--global-config'];
|
||||||
customPath = argv['--global-config'];
|
|
||||||
} catch (_error) {
|
|
||||||
// args are optional so consume error
|
|
||||||
}
|
|
||||||
|
|
||||||
const vercelDirectories = XDGAppPaths('com.vercel.cli').dataDirs();
|
const vercelDirectories = XDGAppPaths('com.vercel.cli').dataDirs();
|
||||||
|
|
||||||
|
|||||||
@@ -7,12 +7,8 @@ import getArgs from '../../util/get-args';
|
|||||||
export default function getLocalPathConfig(prefix: string) {
|
export default function getLocalPathConfig(prefix: string) {
|
||||||
let customPath: string | undefined;
|
let customPath: string | undefined;
|
||||||
|
|
||||||
try {
|
const argv = getArgs(process.argv.slice(2), {}, { permissive: true });
|
||||||
const argv = getArgs(process.argv.slice(2), {});
|
customPath = argv['--local-config'];
|
||||||
customPath = argv['--local-config'];
|
|
||||||
} catch (_error) {
|
|
||||||
// args are optional so consume error
|
|
||||||
}
|
|
||||||
|
|
||||||
// If `--local-config` flag was specified, then that takes priority
|
// If `--local-config` flag was specified, then that takes priority
|
||||||
if (customPath) {
|
if (customPath) {
|
||||||
|
|||||||
@@ -1358,13 +1358,20 @@ export default class DevServer {
|
|||||||
this.cwd,
|
this.cwd,
|
||||||
this.output
|
this.output
|
||||||
);
|
);
|
||||||
for (let plugin of devMiddlewarePlugins) {
|
try {
|
||||||
const result = await plugin.plugin.runDevMiddleware(req, res, this.cwd);
|
for (let plugin of devMiddlewarePlugins) {
|
||||||
if (result.finished) {
|
const result = await plugin.plugin.runDevMiddleware(req, res, this.cwd);
|
||||||
return result;
|
if (result.finished) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return { finished: false };
|
||||||
|
} catch (e) {
|
||||||
|
return {
|
||||||
|
finished: true,
|
||||||
|
error: e,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
return { finished: false };
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2154,7 +2161,10 @@ export default class DevServer {
|
|||||||
process.stdout.write(data.replace(proxyPort, devPort));
|
process.stdout.write(data.replace(proxyPort, devPort));
|
||||||
});
|
});
|
||||||
|
|
||||||
p.on('exit', () => {
|
p.on('exit', (code: number) => {
|
||||||
|
if (code > 0) {
|
||||||
|
process.exit(code);
|
||||||
|
}
|
||||||
this.devProcessPort = undefined;
|
this.devProcessPort = undefined;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
import { Stats } from 'fs';
|
import { Stats } from 'fs';
|
||||||
import { sep, dirname, join, resolve } from 'path';
|
import { sep, dirname, join, resolve } from 'path';
|
||||||
import { readJSON, lstat, readlink, readFile, realpath } from 'fs-extra';
|
import { lstat, readlink, readFile, realpath } from 'fs-extra';
|
||||||
import { isCanary } from './is-canary';
|
import { isCanary } from './is-canary';
|
||||||
import { getPkgName } from './pkg-name';
|
import { getPkgName } from './pkg-name';
|
||||||
|
|
||||||
// `npm` tacks a bunch of extra properties on the `package.json` file,
|
|
||||||
// so check for one of them to determine yarn vs. npm.
|
|
||||||
async function isYarn(): Promise<boolean> {
|
async function isYarn(): Promise<boolean> {
|
||||||
let s: Stats;
|
let s: Stats;
|
||||||
let binPath = process.argv[1];
|
let binPath = process.argv[1];
|
||||||
@@ -20,8 +18,12 @@ async function isYarn(): Promise<boolean> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
const pkgPath = join(dirname(binPath), '..', 'package.json');
|
const pkgPath = join(dirname(binPath), '..', 'package.json');
|
||||||
const pkg = await readJSON(pkgPath).catch(() => ({}));
|
/*
|
||||||
return !('_id' in pkg);
|
* Generally, pkgPath looks like:
|
||||||
|
* "/Users/username/.config/yarn/global/node_modules/vercel/package.json"
|
||||||
|
* "/usr/local/share/.config/yarn/global/node_modules/vercel/package.json"
|
||||||
|
*/
|
||||||
|
return pkgPath.includes(join('yarn', 'global'));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getConfigPrefix() {
|
async function getConfigPrefix() {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import crypto from 'crypto';
|
import crypto from 'crypto';
|
||||||
import ua from 'universal-analytics';
|
import ua from 'universal-analytics';
|
||||||
import { platform, release, userInfo } from 'os';
|
|
||||||
import { getPlatformEnv } from '@vercel/build-utils';
|
import { getPlatformEnv } from '@vercel/build-utils';
|
||||||
|
|
||||||
import userAgent from './ua-browser';
|
import userAgent from './ua-browser';
|
||||||
@@ -16,10 +15,15 @@ export const shouldCollectMetrics =
|
|||||||
|
|
||||||
export const metrics = (): ua.Visitor => {
|
export const metrics = (): ua.Visitor => {
|
||||||
const token =
|
const token =
|
||||||
typeof config.token === 'string' ? config.token : platform() + release();
|
typeof config.token === 'string'
|
||||||
const salt = userInfo().username;
|
? config.token
|
||||||
|
: process.platform + process.arch;
|
||||||
|
const salt =
|
||||||
|
(process.env.USER || '') +
|
||||||
|
(process.env.LANG || '') +
|
||||||
|
(process.env.SHELL || '');
|
||||||
const hash = crypto
|
const hash = crypto
|
||||||
.pbkdf2Sync(token, salt, 1000, 64, 'sha512')
|
.pbkdf2Sync(token, salt, 100, 64, 'sha512')
|
||||||
.toString('hex')
|
.toString('hex')
|
||||||
.substring(0, 24);
|
.substring(0, 24);
|
||||||
|
|
||||||
|
|||||||
@@ -125,10 +125,14 @@ export class Output {
|
|||||||
this.debug(`Spinner invoked (${message}) with a ${delay}ms delay`);
|
this.debug(`Spinner invoked (${message}) with a ${delay}ms delay`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this._spinner) {
|
if (this.isTTY) {
|
||||||
this._spinner.text = message;
|
if (this._spinner) {
|
||||||
|
this._spinner.text = message;
|
||||||
|
} else {
|
||||||
|
this._spinner = wait(message, delay);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
this._spinner = wait(message, delay);
|
this.print(`${message}\n`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -244,20 +244,27 @@ export async function linkFolderToProject(
|
|||||||
try {
|
try {
|
||||||
const gitIgnorePath = join(path, '.gitignore');
|
const gitIgnorePath = join(path, '.gitignore');
|
||||||
|
|
||||||
const gitIgnore = await readFile(gitIgnorePath, 'utf8').catch(() => null);
|
let gitIgnore =
|
||||||
const EOL = gitIgnore && gitIgnore.includes('\r\n') ? '\r\n' : os.EOL;
|
(await readFile(gitIgnorePath, 'utf8').catch(() => null)) ?? '';
|
||||||
|
const EOL = gitIgnore.includes('\r\n') ? '\r\n' : os.EOL;
|
||||||
|
let contentModified = false;
|
||||||
|
|
||||||
if (
|
if (!gitIgnore.split(EOL).includes(VERCEL_DIR)) {
|
||||||
!gitIgnore ||
|
gitIgnore += `${
|
||||||
!gitIgnore.split(EOL).includes(VERCEL_DIR) ||
|
gitIgnore.endsWith(EOL) || gitIgnore.length === 0 ? '' : EOL
|
||||||
!gitIgnore.split(EOL).includes(VERCEL_OUTPUT_DIR)
|
}${VERCEL_DIR}${EOL}`;
|
||||||
) {
|
contentModified = true;
|
||||||
await writeFile(
|
}
|
||||||
gitIgnorePath,
|
|
||||||
gitIgnore
|
if (!gitIgnore.split(EOL).includes(VERCEL_OUTPUT_DIR)) {
|
||||||
? `${gitIgnore}${EOL}${VERCEL_DIR}${EOL}${VERCEL_OUTPUT_DIR}${EOL}`
|
gitIgnore += `${
|
||||||
: `${VERCEL_DIR}${EOL}${VERCEL_OUTPUT_DIR}${EOL}`
|
gitIgnore.endsWith(EOL) || gitIgnore.length === 0 ? '' : EOL
|
||||||
);
|
}${VERCEL_OUTPUT_DIR}${EOL}`;
|
||||||
|
contentModified = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (contentModified) {
|
||||||
|
await writeFile(gitIgnorePath, gitIgnore);
|
||||||
isGitIgnoreUpdated = true;
|
isGitIgnoreUpdated = true;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
3
packages/cli/test/fixtures/unit/edge-middleware-invalid-response/_middleware.js
vendored
Normal file
3
packages/cli/test/fixtures/unit/edge-middleware-invalid-response/_middleware.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export default function () {
|
||||||
|
return 'freecandy';
|
||||||
|
}
|
||||||
@@ -19,7 +19,7 @@ const getRevertAliasConfigFile = () => {
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
module.exports = async function prepare(session) {
|
module.exports = async function prepare(session, binaryPath) {
|
||||||
const spec = {
|
const spec = {
|
||||||
'static-single-file': {
|
'static-single-file': {
|
||||||
'first.png': getImageFile(session, { size: 30 }),
|
'first.png': getImageFile(session, { size: 30 }),
|
||||||
@@ -114,6 +114,23 @@ module.exports = async function prepare(session) {
|
|||||||
2
|
2
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
'dev-fail-on-recursion-command': {
|
||||||
|
'package.json': '{}',
|
||||||
|
},
|
||||||
|
'build-fail-on-recursion-command': {
|
||||||
|
'package.json': '{}',
|
||||||
|
},
|
||||||
|
'build-fail-on-recursion-script': {
|
||||||
|
'package.json': JSON.stringify(
|
||||||
|
{
|
||||||
|
scripts: {
|
||||||
|
build: `${binaryPath} build`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
2
|
||||||
|
),
|
||||||
|
},
|
||||||
'static-deployment': {
|
'static-deployment': {
|
||||||
'index.txt': 'Hello World',
|
'index.txt': 'Hello World',
|
||||||
},
|
},
|
||||||
@@ -348,6 +365,10 @@ module.exports = async function prepare(session) {
|
|||||||
'project-link-dev': {
|
'project-link-dev': {
|
||||||
'package.json': '{}',
|
'package.json': '{}',
|
||||||
},
|
},
|
||||||
|
'project-link-gitignore': {
|
||||||
|
'package.json': '{}',
|
||||||
|
'.gitignore': '.output',
|
||||||
|
},
|
||||||
'project-link-legacy': {
|
'project-link-legacy': {
|
||||||
'index.html': 'Hello',
|
'index.html': 'Hello',
|
||||||
'vercel.json': '{"builds":[{"src":"*.html","use":"@vercel/static"}]}',
|
'vercel.json': '{"builds":[{"src":"*.html","use":"@vercel/static"}]}',
|
||||||
|
|||||||
396
packages/cli/test/integration.js
vendored
396
packages/cli/test/integration.js
vendored
@@ -252,10 +252,69 @@ const createUser = async () => {
|
|||||||
|
|
||||||
const getConfigAuthPath = () => path.join(globalDir, 'auth.json');
|
const getConfigAuthPath = () => path.join(globalDir, 'auth.json');
|
||||||
|
|
||||||
|
async function setupProject(process, projectName, overrides) {
|
||||||
|
await waitForPrompt(process, chunk => /Set up [^?]+\?/.test(chunk));
|
||||||
|
process.stdin.write('yes\n');
|
||||||
|
|
||||||
|
await waitForPrompt(process, chunk => /Which scope [^?]+\?/.test(chunk));
|
||||||
|
process.stdin.write('\n');
|
||||||
|
|
||||||
|
await waitForPrompt(process, chunk =>
|
||||||
|
chunk.includes('Link to existing project?')
|
||||||
|
);
|
||||||
|
process.stdin.write('no\n');
|
||||||
|
|
||||||
|
await waitForPrompt(process, chunk =>
|
||||||
|
chunk.includes('What’s your project’s name?')
|
||||||
|
);
|
||||||
|
process.stdin.write(`${projectName}\n`);
|
||||||
|
|
||||||
|
await waitForPrompt(process, chunk =>
|
||||||
|
chunk.includes('In which directory is your code located?')
|
||||||
|
);
|
||||||
|
process.stdin.write('\n');
|
||||||
|
|
||||||
|
await waitForPrompt(process, chunk =>
|
||||||
|
chunk.includes('Want to override the settings?')
|
||||||
|
);
|
||||||
|
|
||||||
|
if (overrides) {
|
||||||
|
process.stdin.write('yes\n');
|
||||||
|
|
||||||
|
const { buildCommand, outputDirectory, devCommand } = overrides;
|
||||||
|
|
||||||
|
await waitForPrompt(process, chunk =>
|
||||||
|
chunk.includes(
|
||||||
|
'Which settings would you like to overwrite (select multiple)?'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
process.stdin.write('a\n'); // 'a' means select all
|
||||||
|
|
||||||
|
await waitForPrompt(process, chunk =>
|
||||||
|
chunk.includes(`What's your Build Command?`)
|
||||||
|
);
|
||||||
|
process.stdin.write(`${buildCommand ?? ''}\n`);
|
||||||
|
|
||||||
|
await waitForPrompt(process, chunk =>
|
||||||
|
chunk.includes(`What's your Output Directory?`)
|
||||||
|
);
|
||||||
|
process.stdin.write(`${outputDirectory ?? ''}\n`);
|
||||||
|
|
||||||
|
await waitForPrompt(process, chunk =>
|
||||||
|
chunk.includes(`What's your Development Command?`)
|
||||||
|
);
|
||||||
|
process.stdin.write(`${devCommand ?? ''}\n`);
|
||||||
|
} else {
|
||||||
|
process.stdin.write('no\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
await waitForPrompt(process, chunk => chunk.includes('Linked to'));
|
||||||
|
}
|
||||||
|
|
||||||
test.before(async () => {
|
test.before(async () => {
|
||||||
try {
|
try {
|
||||||
await createUser();
|
await createUser();
|
||||||
await prepareFixtures(contextName);
|
await prepareFixtures(contextName, binaryPath);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log('Failed `test.before`');
|
console.log('Failed `test.before`');
|
||||||
console.log(err);
|
console.log(err);
|
||||||
@@ -2227,13 +2286,93 @@ test('whoami', async t => {
|
|||||||
t.is(stdout, contextName, formatOutput({ stdout, stderr }));
|
t.is(stdout, contextName, formatOutput({ stdout, stderr }));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('fail `now dev` dev script without now.json', async t => {
|
test('[vercel dev] fails when dev script calls vercel dev recursively', async t => {
|
||||||
const deploymentPath = fixture('now-dev-fail-dev-script');
|
const deploymentPath = fixture('now-dev-fail-dev-script');
|
||||||
const { exitCode, stderr } = await execute(['dev', deploymentPath]);
|
const { exitCode, stderr } = await execute(['dev', deploymentPath]);
|
||||||
|
|
||||||
t.is(exitCode, 1);
|
t.is(exitCode, 1);
|
||||||
t.true(
|
t.true(
|
||||||
stderr.includes('must not contain `now dev`'),
|
stderr.includes('must not recursively invoke itself'),
|
||||||
|
`Received instead: "${stderr}"`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('[vercel dev] fails when development commad calls vercel dev recursively', async t => {
|
||||||
|
const dir = fixture('dev-fail-on-recursion-command');
|
||||||
|
const projectName = `dev-fail-on-recursion-command-${
|
||||||
|
Math.random().toString(36).split('.')[1]
|
||||||
|
}`;
|
||||||
|
|
||||||
|
const dev = execa(binaryPath, ['dev', ...defaultArgs], {
|
||||||
|
cwd: dir,
|
||||||
|
reject: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
await setupProject(dev, projectName, {
|
||||||
|
devCommand: `${binaryPath} dev`,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { exitCode, stderr } = await dev;
|
||||||
|
|
||||||
|
t.is(exitCode, 1);
|
||||||
|
t.true(
|
||||||
|
stderr.includes('must not recursively invoke itself'),
|
||||||
|
`Received instead: "${stderr}"`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('[vercel build] fails when build commad calls vercel build recursively', async t => {
|
||||||
|
const dir = fixture('build-fail-on-recursion-command');
|
||||||
|
const projectName = `build-fail-on-recursion-command-${
|
||||||
|
Math.random().toString(36).split('.')[1]
|
||||||
|
}`;
|
||||||
|
|
||||||
|
const build = execa(binaryPath, ['build', ...defaultArgs], {
|
||||||
|
cwd: dir,
|
||||||
|
reject: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
await waitForPrompt(build, chunk =>
|
||||||
|
chunk.includes('No Project Settings found locally')
|
||||||
|
);
|
||||||
|
build.stdin.write('yes\n');
|
||||||
|
|
||||||
|
await setupProject(build, projectName, {
|
||||||
|
buildCommand: `${binaryPath} build`,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { exitCode, stderr } = await build;
|
||||||
|
|
||||||
|
t.is(exitCode, 1);
|
||||||
|
t.true(
|
||||||
|
stderr.includes('must not recursively invoke itself'),
|
||||||
|
`Received instead: "${stderr}"`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('[vercel build] fails when build script calls vercel build recursively', async t => {
|
||||||
|
const dir = fixture('build-fail-on-recursion-script');
|
||||||
|
const projectName = `build-fail-on-recursion-script-${
|
||||||
|
Math.random().toString(36).split('.')[1]
|
||||||
|
}`;
|
||||||
|
|
||||||
|
const build = execa(binaryPath, ['build', ...defaultArgs], {
|
||||||
|
cwd: dir,
|
||||||
|
reject: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
await waitForPrompt(build, chunk =>
|
||||||
|
chunk.includes('No Project Settings found locally')
|
||||||
|
);
|
||||||
|
build.stdin.write('yes\n');
|
||||||
|
|
||||||
|
await setupProject(build, projectName);
|
||||||
|
|
||||||
|
const { exitCode, stderr } = await build;
|
||||||
|
|
||||||
|
t.is(exitCode, 1);
|
||||||
|
t.true(
|
||||||
|
stderr.includes('must not recursively invoke itself'),
|
||||||
`Received instead: "${stderr}"`
|
`Received instead: "${stderr}"`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -2544,7 +2683,7 @@ test('deploy a Lambda with 3 seconds of maxDuration', async t => {
|
|||||||
const url = new URL(output.stdout);
|
const url = new URL(output.stdout);
|
||||||
|
|
||||||
// Should time out
|
// Should time out
|
||||||
url.pathname = '/api/wait-for/4';
|
url.pathname = '/api/wait-for/5';
|
||||||
const response1 = await fetch(url.href);
|
const response1 = await fetch(url.href);
|
||||||
t.is(
|
t.is(
|
||||||
response1.status,
|
response1.status,
|
||||||
@@ -2553,7 +2692,7 @@ test('deploy a Lambda with 3 seconds of maxDuration', async t => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Should not time out
|
// Should not time out
|
||||||
url.pathname = '/api/wait-for/2';
|
url.pathname = '/api/wait-for/1';
|
||||||
const response2 = await fetch(url.href);
|
const response2 = await fetch(url.href);
|
||||||
t.is(
|
t.is(
|
||||||
response2.status,
|
response2.status,
|
||||||
@@ -2683,59 +2822,10 @@ test('should show prompts to set up project during first deploy', async t => {
|
|||||||
|
|
||||||
const now = execa(binaryPath, [dir, ...defaultArgs]);
|
const now = execa(binaryPath, [dir, ...defaultArgs]);
|
||||||
|
|
||||||
await waitForPrompt(now, chunk => /Set up and deploy [^?]+\?/.test(chunk));
|
await setupProject(now, projectName, {
|
||||||
now.stdin.write('yes\n');
|
buildCommand: `mkdir -p o && echo '<h1>custom hello</h1>' > o/index.html`,
|
||||||
|
outputDirectory: 'o',
|
||||||
await waitForPrompt(now, chunk =>
|
});
|
||||||
chunk.includes('Which scope do you want to deploy to?')
|
|
||||||
);
|
|
||||||
now.stdin.write('\n');
|
|
||||||
|
|
||||||
await waitForPrompt(now, chunk =>
|
|
||||||
chunk.includes('Link to existing project?')
|
|
||||||
);
|
|
||||||
now.stdin.write('no\n');
|
|
||||||
|
|
||||||
await waitForPrompt(now, chunk =>
|
|
||||||
chunk.includes('What’s your project’s name?')
|
|
||||||
);
|
|
||||||
now.stdin.write(`${projectName}\n`);
|
|
||||||
|
|
||||||
await waitForPrompt(now, chunk =>
|
|
||||||
chunk.includes('In which directory is your code located?')
|
|
||||||
);
|
|
||||||
now.stdin.write('\n');
|
|
||||||
|
|
||||||
await waitForPrompt(now, chunk =>
|
|
||||||
chunk.includes('Want to override the settings?')
|
|
||||||
);
|
|
||||||
now.stdin.write('yes\n');
|
|
||||||
|
|
||||||
await waitForPrompt(now, chunk =>
|
|
||||||
chunk.includes(
|
|
||||||
'Which settings would you like to overwrite (select multiple)?'
|
|
||||||
)
|
|
||||||
);
|
|
||||||
now.stdin.write('a\n'); // 'a' means select all
|
|
||||||
|
|
||||||
await waitForPrompt(now, chunk =>
|
|
||||||
chunk.includes(`What's your Build Command?`)
|
|
||||||
);
|
|
||||||
now.stdin.write(
|
|
||||||
`mkdir -p o && echo '<h1>custom hello</h1>' > o/index.html\n`
|
|
||||||
);
|
|
||||||
|
|
||||||
await waitForPrompt(now, chunk =>
|
|
||||||
chunk.includes(`What's your Output Directory?`)
|
|
||||||
);
|
|
||||||
now.stdin.write(`o\n`);
|
|
||||||
|
|
||||||
await waitForPrompt(now, chunk =>
|
|
||||||
chunk.includes(`What's your Development Command?`)
|
|
||||||
);
|
|
||||||
now.stdin.write(`\n`);
|
|
||||||
|
|
||||||
await waitForPrompt(now, chunk => chunk.includes('Linked to'));
|
|
||||||
|
|
||||||
const output = await now;
|
const output = await now;
|
||||||
|
|
||||||
@@ -3301,55 +3391,10 @@ test('[vc link] should show prompts to set up project', async t => {
|
|||||||
|
|
||||||
const vc = execa(binaryPath, ['link', ...defaultArgs], { cwd: dir });
|
const vc = execa(binaryPath, ['link', ...defaultArgs], { cwd: dir });
|
||||||
|
|
||||||
await waitForPrompt(vc, chunk => /Set up [^?]+\?/.test(chunk));
|
await setupProject(vc, projectName, {
|
||||||
vc.stdin.write('yes\n');
|
buildCommand: `mkdir -p o && echo '<h1>custom hello</h1>' > o/index.html`,
|
||||||
|
outputDirectory: 'o',
|
||||||
await waitForPrompt(vc, chunk =>
|
});
|
||||||
chunk.includes('Which scope should contain your project?')
|
|
||||||
);
|
|
||||||
vc.stdin.write('\n');
|
|
||||||
|
|
||||||
await waitForPrompt(vc, chunk => chunk.includes('Link to existing project?'));
|
|
||||||
vc.stdin.write('no\n');
|
|
||||||
|
|
||||||
await waitForPrompt(vc, chunk =>
|
|
||||||
chunk.includes('What’s your project’s name?')
|
|
||||||
);
|
|
||||||
vc.stdin.write(`${projectName}\n`);
|
|
||||||
|
|
||||||
await waitForPrompt(vc, chunk =>
|
|
||||||
chunk.includes('In which directory is your code located?')
|
|
||||||
);
|
|
||||||
vc.stdin.write('\n');
|
|
||||||
|
|
||||||
await waitForPrompt(vc, chunk =>
|
|
||||||
chunk.includes('Want to override the settings?')
|
|
||||||
);
|
|
||||||
vc.stdin.write('yes\n');
|
|
||||||
|
|
||||||
await waitForPrompt(vc, chunk =>
|
|
||||||
chunk.includes(
|
|
||||||
'Which settings would you like to overwrite (select multiple)?'
|
|
||||||
)
|
|
||||||
);
|
|
||||||
vc.stdin.write('a\n'); // 'a' means select all
|
|
||||||
|
|
||||||
await waitForPrompt(vc, chunk =>
|
|
||||||
chunk.includes(`What's your Build Command?`)
|
|
||||||
);
|
|
||||||
vc.stdin.write(`mkdir -p o && echo '<h1>custom hello</h1>' > o/index.html\n`);
|
|
||||||
|
|
||||||
await waitForPrompt(vc, chunk =>
|
|
||||||
chunk.includes(`What's your Output Directory?`)
|
|
||||||
);
|
|
||||||
vc.stdin.write(`o\n`);
|
|
||||||
|
|
||||||
await waitForPrompt(vc, chunk =>
|
|
||||||
chunk.includes(`What's your Development Command?`)
|
|
||||||
);
|
|
||||||
vc.stdin.write(`\n`);
|
|
||||||
|
|
||||||
await waitForPrompt(vc, chunk => chunk.includes('Linked to'));
|
|
||||||
|
|
||||||
const output = await vc;
|
const output = await vc;
|
||||||
|
|
||||||
@@ -3408,6 +3453,29 @@ test('[vc link --confirm] should not show prompts and autolink', async t => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('[vc link] should not duplicate paths in .gitignore', async t => {
|
||||||
|
const dir = fixture('project-link-gitignore');
|
||||||
|
|
||||||
|
// remove previously linked project if it exists
|
||||||
|
await remove(path.join(dir, '.vercel'));
|
||||||
|
|
||||||
|
const { exitCode, stderr, stdout } = await execa(
|
||||||
|
binaryPath,
|
||||||
|
['link', '--confirm', ...defaultArgs],
|
||||||
|
{ cwd: dir, reject: false }
|
||||||
|
);
|
||||||
|
|
||||||
|
// Ensure the exit code is right
|
||||||
|
t.is(exitCode, 0, formatOutput({ stderr, stdout }));
|
||||||
|
|
||||||
|
// Ensure the message is correct pattern
|
||||||
|
t.regex(stderr, /Linked to /m);
|
||||||
|
|
||||||
|
// Ensure .gitignore is created
|
||||||
|
const gitignore = await readFile(path.join(dir, '.gitignore'), 'utf8');
|
||||||
|
t.is(gitignore, '.output\n.vercel\n');
|
||||||
|
});
|
||||||
|
|
||||||
test('[vc dev] should show prompts to set up project', async t => {
|
test('[vc dev] should show prompts to set up project', async t => {
|
||||||
const dir = fixture('project-link-dev');
|
const dir = fixture('project-link-dev');
|
||||||
const port = 58352;
|
const port = 58352;
|
||||||
@@ -3422,59 +3490,10 @@ test('[vc dev] should show prompts to set up project', async t => {
|
|||||||
cwd: dir,
|
cwd: dir,
|
||||||
});
|
});
|
||||||
|
|
||||||
await waitForPrompt(dev, chunk => /Set up and develop [^?]+\?/.test(chunk));
|
await setupProject(dev, projectName, {
|
||||||
dev.stdin.write('yes\n');
|
buildCommand: `mkdir -p o && echo '<h1>custom hello</h1>' > o/index.html`,
|
||||||
|
outputDirectory: 'o',
|
||||||
await waitForPrompt(dev, chunk =>
|
});
|
||||||
chunk.includes('Which scope should contain your project?')
|
|
||||||
);
|
|
||||||
dev.stdin.write('\n');
|
|
||||||
|
|
||||||
await waitForPrompt(dev, chunk =>
|
|
||||||
chunk.includes('Link to existing project?')
|
|
||||||
);
|
|
||||||
dev.stdin.write('no\n');
|
|
||||||
|
|
||||||
await waitForPrompt(dev, chunk =>
|
|
||||||
chunk.includes('What’s your project’s name?')
|
|
||||||
);
|
|
||||||
dev.stdin.write(`${projectName}\n`);
|
|
||||||
|
|
||||||
await waitForPrompt(dev, chunk =>
|
|
||||||
chunk.includes('In which directory is your code located?')
|
|
||||||
);
|
|
||||||
dev.stdin.write('\n');
|
|
||||||
|
|
||||||
await waitForPrompt(dev, chunk =>
|
|
||||||
chunk.includes('Want to override the settings?')
|
|
||||||
);
|
|
||||||
dev.stdin.write('yes\n');
|
|
||||||
|
|
||||||
await waitForPrompt(dev, chunk =>
|
|
||||||
chunk.includes(
|
|
||||||
'Which settings would you like to overwrite (select multiple)?'
|
|
||||||
)
|
|
||||||
);
|
|
||||||
dev.stdin.write('a\n'); // 'a' means select all
|
|
||||||
|
|
||||||
await waitForPrompt(dev, chunk =>
|
|
||||||
chunk.includes(`What's your Build Command?`)
|
|
||||||
);
|
|
||||||
dev.stdin.write(
|
|
||||||
`mkdir -p o && echo '<h1>custom hello</h1>' > o/index.html\n`
|
|
||||||
);
|
|
||||||
|
|
||||||
await waitForPrompt(dev, chunk =>
|
|
||||||
chunk.includes(`What's your Output Directory?`)
|
|
||||||
);
|
|
||||||
dev.stdin.write(`o\n`);
|
|
||||||
|
|
||||||
await waitForPrompt(dev, chunk =>
|
|
||||||
chunk.includes(`What's your Development Command?`)
|
|
||||||
);
|
|
||||||
dev.stdin.write(`\n`);
|
|
||||||
|
|
||||||
await waitForPrompt(dev, chunk => chunk.includes('Linked to'));
|
|
||||||
|
|
||||||
// Ensure .gitignore is created
|
// Ensure .gitignore is created
|
||||||
const gitignore = await readFile(path.join(dir, '.gitignore'), 'utf8');
|
const gitignore = await readFile(path.join(dir, '.gitignore'), 'utf8');
|
||||||
@@ -3574,59 +3593,12 @@ test('[vc dev] should send the platform proxy request headers to frontend dev se
|
|||||||
cwd: dir,
|
cwd: dir,
|
||||||
});
|
});
|
||||||
|
|
||||||
await waitForPrompt(dev, chunk => /Set up and develop [^?]+\?/.test(chunk));
|
await setupProject(dev, projectName, {
|
||||||
dev.stdin.write('yes\n');
|
buildCommand: `mkdir -p o && echo '<h1>custom hello</h1>' > o/index.html`,
|
||||||
|
outputDirectory: 'o',
|
||||||
|
devCommand: 'node server.js',
|
||||||
|
});
|
||||||
|
|
||||||
await waitForPrompt(dev, chunk =>
|
|
||||||
chunk.includes('Which scope should contain your project?')
|
|
||||||
);
|
|
||||||
dev.stdin.write('\n');
|
|
||||||
|
|
||||||
await waitForPrompt(dev, chunk =>
|
|
||||||
chunk.includes('Link to existing project?')
|
|
||||||
);
|
|
||||||
dev.stdin.write('no\n');
|
|
||||||
|
|
||||||
await waitForPrompt(dev, chunk =>
|
|
||||||
chunk.includes('What’s your project’s name?')
|
|
||||||
);
|
|
||||||
dev.stdin.write(`${projectName}\n`);
|
|
||||||
|
|
||||||
await waitForPrompt(dev, chunk =>
|
|
||||||
chunk.includes('In which directory is your code located?')
|
|
||||||
);
|
|
||||||
dev.stdin.write('\n');
|
|
||||||
|
|
||||||
await waitForPrompt(dev, chunk =>
|
|
||||||
chunk.includes('Want to override the settings?')
|
|
||||||
);
|
|
||||||
dev.stdin.write('yes\n');
|
|
||||||
|
|
||||||
await waitForPrompt(dev, chunk =>
|
|
||||||
chunk.includes(
|
|
||||||
'Which settings would you like to overwrite (select multiple)?'
|
|
||||||
)
|
|
||||||
);
|
|
||||||
dev.stdin.write('a\n'); // 'a' means select all
|
|
||||||
|
|
||||||
await waitForPrompt(dev, chunk =>
|
|
||||||
chunk.includes(`What's your Build Command?`)
|
|
||||||
);
|
|
||||||
dev.stdin.write(
|
|
||||||
`mkdir -p o && echo '<h1>custom hello</h1>' > o/index.html\n`
|
|
||||||
);
|
|
||||||
|
|
||||||
await waitForPrompt(dev, chunk =>
|
|
||||||
chunk.includes(`What's your Output Directory?`)
|
|
||||||
);
|
|
||||||
dev.stdin.write(`o\n`);
|
|
||||||
|
|
||||||
await waitForPrompt(dev, chunk =>
|
|
||||||
chunk.includes(`What's your Development Command?`)
|
|
||||||
);
|
|
||||||
dev.stdin.write(`node server.js\n`);
|
|
||||||
|
|
||||||
await waitForPrompt(dev, chunk => chunk.includes('Linked to'));
|
|
||||||
await waitForPrompt(dev, chunk => chunk.includes('Ready! Available at'));
|
await waitForPrompt(dev, chunk => chunk.includes('Ready! Available at'));
|
||||||
|
|
||||||
// Ensure that `vc dev` also works
|
// Ensure that `vc dev` also works
|
||||||
|
|||||||
@@ -374,4 +374,15 @@ describe('DevServer', () => {
|
|||||||
);
|
);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
it(
|
||||||
|
'should render an error page when the middleware returns not a Response',
|
||||||
|
testFixture('edge-middleware-invalid-response', async server => {
|
||||||
|
const response = await fetch(`${server.address}/index.html`);
|
||||||
|
const body = await response.text();
|
||||||
|
expect(body).toStrictEqual(
|
||||||
|
'A server error has occurred\n\nEDGE_FUNCTION_INVOCATION_FAILED\n'
|
||||||
|
);
|
||||||
|
})
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ describe('getUpdateCommand', () => {
|
|||||||
it('should detect update command', async () => {
|
it('should detect update command', async () => {
|
||||||
const updateCommand = await getUpdateCommand();
|
const updateCommand = await getUpdateCommand();
|
||||||
expect(updateCommand).toEqual(
|
expect(updateCommand).toEqual(
|
||||||
`yarn add vercel@${isCanary() ? 'canary' : 'latest'}`
|
`npm i vercel@${isCanary() ? 'canary' : 'latest'}`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vercel/client",
|
"name": "@vercel/client",
|
||||||
"version": "10.2.3-canary.18",
|
"version": "10.2.3-canary.21",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"typings": "dist/index.d.ts",
|
"typings": "dist/index.d.ts",
|
||||||
"homepage": "https://vercel.com",
|
"homepage": "https://vercel.com",
|
||||||
@@ -40,7 +40,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vercel/build-utils": "2.12.3-canary.17",
|
"@vercel/build-utils": "2.12.3-canary.20",
|
||||||
"@zeit/fetch": "5.2.0",
|
"@zeit/fetch": "5.2.0",
|
||||||
"async-retry": "1.2.3",
|
"async-retry": "1.2.3",
|
||||||
"async-sema": "3.0.0",
|
"async-sema": "3.0.0",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vercel/frameworks",
|
"name": "@vercel/frameworks",
|
||||||
"version": "0.5.1-canary.11",
|
"version": "0.5.1-canary.12",
|
||||||
"main": "./dist/frameworks.js",
|
"main": "./dist/frameworks.js",
|
||||||
"types": "./dist/frameworks.d.ts",
|
"types": "./dist/frameworks.d.ts",
|
||||||
"files": [
|
"files": [
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ export const frameworks = [
|
|||||||
},
|
},
|
||||||
buildCommand: {
|
buildCommand: {
|
||||||
placeholder: '`npm run build` or `blitz build`',
|
placeholder: '`npm run build` or `blitz build`',
|
||||||
|
value: 'blitz build',
|
||||||
},
|
},
|
||||||
devCommand: {
|
devCommand: {
|
||||||
value: 'blitz start',
|
value: 'blitz start',
|
||||||
@@ -51,8 +52,6 @@ export const frameworks = [
|
|||||||
placeholder: 'Next.js default',
|
placeholder: 'Next.js default',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
devCommand: 'blitz start',
|
|
||||||
buildCommand: 'blitz build',
|
|
||||||
getFsOutputDir: async () => '.next',
|
getFsOutputDir: async () => '.next',
|
||||||
getOutputDirName: async () => 'public',
|
getOutputDirName: async () => 'public',
|
||||||
},
|
},
|
||||||
@@ -83,6 +82,7 @@ export const frameworks = [
|
|||||||
},
|
},
|
||||||
buildCommand: {
|
buildCommand: {
|
||||||
placeholder: '`npm run build` or `next build`',
|
placeholder: '`npm run build` or `next build`',
|
||||||
|
value: 'next build',
|
||||||
},
|
},
|
||||||
devCommand: {
|
devCommand: {
|
||||||
value: 'next dev --port $PORT',
|
value: 'next dev --port $PORT',
|
||||||
@@ -98,8 +98,6 @@ export const frameworks = [
|
|||||||
dependencies: ['next-plugin-sentry', 'next-sentry-source-maps'],
|
dependencies: ['next-plugin-sentry', 'next-sentry-source-maps'],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
devCommand: 'next dev --port $PORT',
|
|
||||||
buildCommand: 'next build',
|
|
||||||
getFsOutputDir: async () => '.next',
|
getFsOutputDir: async () => '.next',
|
||||||
getOutputDirName: async () => 'public',
|
getOutputDirName: async () => 'public',
|
||||||
cachePattern: '.next/cache/**',
|
cachePattern: '.next/cache/**',
|
||||||
@@ -131,6 +129,7 @@ export const frameworks = [
|
|||||||
},
|
},
|
||||||
buildCommand: {
|
buildCommand: {
|
||||||
placeholder: '`npm run build` or `gatsby build`',
|
placeholder: '`npm run build` or `gatsby build`',
|
||||||
|
value: 'gatsby build',
|
||||||
},
|
},
|
||||||
devCommand: {
|
devCommand: {
|
||||||
value: 'gatsby develop --port $PORT',
|
value: 'gatsby develop --port $PORT',
|
||||||
@@ -141,8 +140,6 @@ export const frameworks = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
dependency: 'gatsby',
|
dependency: 'gatsby',
|
||||||
devCommand: 'gatsby develop --port $PORT',
|
|
||||||
buildCommand: 'gatsby build',
|
|
||||||
getOutputDirName: async () => 'public',
|
getOutputDirName: async () => 'public',
|
||||||
getFsOutputDir: async () => 'public',
|
getFsOutputDir: async () => 'public',
|
||||||
defaultRoutes: async (dirPrefix: string) => {
|
defaultRoutes: async (dirPrefix: string) => {
|
||||||
@@ -219,6 +216,7 @@ export const frameworks = [
|
|||||||
},
|
},
|
||||||
buildCommand: {
|
buildCommand: {
|
||||||
placeholder: '`npm run build` or `hexo generate`',
|
placeholder: '`npm run build` or `hexo generate`',
|
||||||
|
value: 'hexo generate',
|
||||||
},
|
},
|
||||||
devCommand: {
|
devCommand: {
|
||||||
value: 'hexo server --port $PORT',
|
value: 'hexo server --port $PORT',
|
||||||
@@ -229,8 +227,6 @@ export const frameworks = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
dependency: 'hexo',
|
dependency: 'hexo',
|
||||||
devCommand: 'hexo server --port $PORT',
|
|
||||||
buildCommand: 'hexo generate',
|
|
||||||
getFsOutputDir: async () => 'public',
|
getFsOutputDir: async () => 'public',
|
||||||
getOutputDirName: async () => 'public',
|
getOutputDirName: async () => 'public',
|
||||||
},
|
},
|
||||||
@@ -259,6 +255,7 @@ export const frameworks = [
|
|||||||
},
|
},
|
||||||
buildCommand: {
|
buildCommand: {
|
||||||
placeholder: '`npm run build` or `npx @11ty/eleventy`',
|
placeholder: '`npm run build` or `npx @11ty/eleventy`',
|
||||||
|
value: 'npx @11ty/eleventy',
|
||||||
},
|
},
|
||||||
devCommand: {
|
devCommand: {
|
||||||
value: 'npx @11ty/eleventy --serve --watch --port $PORT',
|
value: 'npx @11ty/eleventy --serve --watch --port $PORT',
|
||||||
@@ -269,8 +266,6 @@ export const frameworks = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
dependency: '@11ty/eleventy',
|
dependency: '@11ty/eleventy',
|
||||||
devCommand: 'npx @11ty/eleventy --serve --watch --port $PORT',
|
|
||||||
buildCommand: 'npx @11ty/eleventy',
|
|
||||||
getFsOutputDir: async () => '_site',
|
getFsOutputDir: async () => '_site',
|
||||||
getOutputDirName: async () => '_site',
|
getOutputDirName: async () => '_site',
|
||||||
cachePattern: '.cache/**',
|
cachePattern: '.cache/**',
|
||||||
@@ -300,6 +295,7 @@ export const frameworks = [
|
|||||||
},
|
},
|
||||||
buildCommand: {
|
buildCommand: {
|
||||||
placeholder: '`npm run build` or `docusaurus build`',
|
placeholder: '`npm run build` or `docusaurus build`',
|
||||||
|
value: 'docusaurus build',
|
||||||
},
|
},
|
||||||
devCommand: {
|
devCommand: {
|
||||||
value: 'docusaurus start --port $PORT',
|
value: 'docusaurus start --port $PORT',
|
||||||
@@ -310,8 +306,6 @@ export const frameworks = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
dependency: '@docusaurus/core',
|
dependency: '@docusaurus/core',
|
||||||
devCommand: 'docusaurus start --port $PORT',
|
|
||||||
buildCommand: 'docusaurus build',
|
|
||||||
getFsOutputDir: async (dirPrefix: string) => {
|
getFsOutputDir: async (dirPrefix: string) => {
|
||||||
const base = 'build';
|
const base = 'build';
|
||||||
try {
|
try {
|
||||||
@@ -456,6 +450,7 @@ export const frameworks = [
|
|||||||
},
|
},
|
||||||
buildCommand: {
|
buildCommand: {
|
||||||
placeholder: '`npm run build` or `docusaurus-build`',
|
placeholder: '`npm run build` or `docusaurus-build`',
|
||||||
|
value: 'docusaurus-build',
|
||||||
},
|
},
|
||||||
devCommand: {
|
devCommand: {
|
||||||
value: 'docusaurus-start --port $PORT',
|
value: 'docusaurus-start --port $PORT',
|
||||||
@@ -466,8 +461,6 @@ export const frameworks = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
dependency: 'docusaurus',
|
dependency: 'docusaurus',
|
||||||
devCommand: 'docusaurus-start --port $PORT',
|
|
||||||
buildCommand: 'docusaurus-build',
|
|
||||||
getFsOutputDir: async (dirPrefix: string) => {
|
getFsOutputDir: async (dirPrefix: string) => {
|
||||||
const base = 'build';
|
const base = 'build';
|
||||||
try {
|
try {
|
||||||
@@ -523,6 +516,7 @@ export const frameworks = [
|
|||||||
},
|
},
|
||||||
buildCommand: {
|
buildCommand: {
|
||||||
placeholder: '`npm run build` or `preact build`',
|
placeholder: '`npm run build` or `preact build`',
|
||||||
|
value: 'preact build',
|
||||||
},
|
},
|
||||||
devCommand: {
|
devCommand: {
|
||||||
value: 'preact watch --port $PORT',
|
value: 'preact watch --port $PORT',
|
||||||
@@ -533,8 +527,6 @@ export const frameworks = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
dependency: 'preact-cli',
|
dependency: 'preact-cli',
|
||||||
devCommand: 'preact watch --port $PORT',
|
|
||||||
buildCommand: 'preact build',
|
|
||||||
getFsOutputDir: async () => 'build',
|
getFsOutputDir: async () => 'build',
|
||||||
getOutputDirName: async () => 'build',
|
getOutputDirName: async () => 'build',
|
||||||
defaultRoutes: [
|
defaultRoutes: [
|
||||||
@@ -581,6 +573,7 @@ export const frameworks = [
|
|||||||
},
|
},
|
||||||
buildCommand: {
|
buildCommand: {
|
||||||
placeholder: '`npm run build` or `dojo build`',
|
placeholder: '`npm run build` or `dojo build`',
|
||||||
|
value: 'dojo build',
|
||||||
},
|
},
|
||||||
devCommand: {
|
devCommand: {
|
||||||
value: 'dojo build -m dev -w -s -p $PORT',
|
value: 'dojo build -m dev -w -s -p $PORT',
|
||||||
@@ -591,8 +584,6 @@ export const frameworks = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
dependency: '@dojo/cli',
|
dependency: '@dojo/cli',
|
||||||
devCommand: 'dojo build -m dev -w -s -p $PORT',
|
|
||||||
buildCommand: 'dojo build',
|
|
||||||
getFsOutputDir: async () => 'output/dist',
|
getFsOutputDir: async () => 'output/dist',
|
||||||
getOutputDirName: async () => join('output', 'dist'),
|
getOutputDirName: async () => join('output', 'dist'),
|
||||||
defaultRoutes: [
|
defaultRoutes: [
|
||||||
@@ -649,6 +640,7 @@ export const frameworks = [
|
|||||||
},
|
},
|
||||||
buildCommand: {
|
buildCommand: {
|
||||||
placeholder: '`npm run build` or `ember build`',
|
placeholder: '`npm run build` or `ember build`',
|
||||||
|
value: 'ember build',
|
||||||
},
|
},
|
||||||
devCommand: {
|
devCommand: {
|
||||||
value: 'ember serve --port $PORT',
|
value: 'ember serve --port $PORT',
|
||||||
@@ -659,8 +651,6 @@ export const frameworks = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
dependency: 'ember-cli',
|
dependency: 'ember-cli',
|
||||||
devCommand: 'ember serve --port $PORT',
|
|
||||||
buildCommand: 'ember build',
|
|
||||||
getFsOutputDir: async () => 'dist',
|
getFsOutputDir: async () => 'dist',
|
||||||
getOutputDirName: async () => 'dist',
|
getOutputDirName: async () => 'dist',
|
||||||
defaultRoutes: [
|
defaultRoutes: [
|
||||||
@@ -705,6 +695,7 @@ export const frameworks = [
|
|||||||
},
|
},
|
||||||
buildCommand: {
|
buildCommand: {
|
||||||
placeholder: '`npm run build` or `vue-cli-service build`',
|
placeholder: '`npm run build` or `vue-cli-service build`',
|
||||||
|
value: 'vue-cli-service build',
|
||||||
},
|
},
|
||||||
devCommand: {
|
devCommand: {
|
||||||
value: 'vue-cli-service serve --port $PORT',
|
value: 'vue-cli-service serve --port $PORT',
|
||||||
@@ -715,8 +706,6 @@ export const frameworks = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
dependency: '@vue/cli-service',
|
dependency: '@vue/cli-service',
|
||||||
devCommand: 'vue-cli-service serve --port $PORT',
|
|
||||||
buildCommand: 'vue-cli-service build',
|
|
||||||
getFsOutputDir: async () => 'dist',
|
getFsOutputDir: async () => 'dist',
|
||||||
getOutputDirName: async () => 'dist',
|
getOutputDirName: async () => 'dist',
|
||||||
defaultRoutes: [
|
defaultRoutes: [
|
||||||
@@ -783,6 +772,7 @@ export const frameworks = [
|
|||||||
},
|
},
|
||||||
buildCommand: {
|
buildCommand: {
|
||||||
placeholder: '`npm run build` or `ng build && scully`',
|
placeholder: '`npm run build` or `ng build && scully`',
|
||||||
|
value: 'ng build && scully',
|
||||||
},
|
},
|
||||||
devCommand: {
|
devCommand: {
|
||||||
value: 'ng serve --port $PORT',
|
value: 'ng serve --port $PORT',
|
||||||
@@ -793,8 +783,6 @@ export const frameworks = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
dependency: '@scullyio/init',
|
dependency: '@scullyio/init',
|
||||||
devCommand: 'ng serve --port $PORT',
|
|
||||||
buildCommand: 'ng build && scully',
|
|
||||||
getFsOutputDir: async () => 'dist',
|
getFsOutputDir: async () => 'dist',
|
||||||
getOutputDirName: async () => 'dist/static',
|
getOutputDirName: async () => 'dist/static',
|
||||||
},
|
},
|
||||||
@@ -822,6 +810,7 @@ export const frameworks = [
|
|||||||
},
|
},
|
||||||
buildCommand: {
|
buildCommand: {
|
||||||
placeholder: '`npm run build` or `ng build`',
|
placeholder: '`npm run build` or `ng build`',
|
||||||
|
value: 'ng build',
|
||||||
},
|
},
|
||||||
devCommand: {
|
devCommand: {
|
||||||
value: 'ng serve --port $PORT',
|
value: 'ng serve --port $PORT',
|
||||||
@@ -831,8 +820,6 @@ export const frameworks = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
dependency: '@ionic/angular',
|
dependency: '@ionic/angular',
|
||||||
devCommand: 'ng serve --port $PORT',
|
|
||||||
buildCommand: 'ng build',
|
|
||||||
getFsOutputDir: async () => 'www',
|
getFsOutputDir: async () => 'www',
|
||||||
getOutputDirName: async () => 'www',
|
getOutputDirName: async () => 'www',
|
||||||
defaultRoutes: [
|
defaultRoutes: [
|
||||||
@@ -876,6 +863,7 @@ export const frameworks = [
|
|||||||
},
|
},
|
||||||
buildCommand: {
|
buildCommand: {
|
||||||
placeholder: '`npm run build` or `ng build`',
|
placeholder: '`npm run build` or `ng build`',
|
||||||
|
value: 'ng build',
|
||||||
},
|
},
|
||||||
devCommand: {
|
devCommand: {
|
||||||
value: 'ng serve --port $PORT',
|
value: 'ng serve --port $PORT',
|
||||||
@@ -886,8 +874,6 @@ export const frameworks = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
dependency: '@angular/cli',
|
dependency: '@angular/cli',
|
||||||
devCommand: 'ng serve --port $PORT',
|
|
||||||
buildCommand: 'ng build',
|
|
||||||
getFsOutputDir: async () => 'dist',
|
getFsOutputDir: async () => 'dist',
|
||||||
getOutputDirName: async (dirPrefix: string) => {
|
getOutputDirName: async (dirPrefix: string) => {
|
||||||
const base = 'dist';
|
const base = 'dist';
|
||||||
@@ -945,6 +931,7 @@ export const frameworks = [
|
|||||||
},
|
},
|
||||||
buildCommand: {
|
buildCommand: {
|
||||||
placeholder: '`npm run build` or `polymer build`',
|
placeholder: '`npm run build` or `polymer build`',
|
||||||
|
value: 'polymer build',
|
||||||
},
|
},
|
||||||
devCommand: {
|
devCommand: {
|
||||||
value: 'polymer serve --port $PORT',
|
value: 'polymer serve --port $PORT',
|
||||||
@@ -955,8 +942,6 @@ export const frameworks = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
dependency: 'polymer-cli',
|
dependency: 'polymer-cli',
|
||||||
devCommand: 'polymer serve --port $PORT',
|
|
||||||
buildCommand: 'polymer build',
|
|
||||||
getFsOutputDir: async () => 'build',
|
getFsOutputDir: async () => 'build',
|
||||||
getOutputDirName: async (dirPrefix: string) => {
|
getOutputDirName: async (dirPrefix: string) => {
|
||||||
const base = 'build';
|
const base = 'build';
|
||||||
@@ -1016,6 +1001,7 @@ export const frameworks = [
|
|||||||
},
|
},
|
||||||
buildCommand: {
|
buildCommand: {
|
||||||
placeholder: '`npm run build` or `rollup -c`',
|
placeholder: '`npm run build` or `rollup -c`',
|
||||||
|
value: 'rollup -c',
|
||||||
},
|
},
|
||||||
devCommand: {
|
devCommand: {
|
||||||
value: 'rollup -c -w',
|
value: 'rollup -c -w',
|
||||||
@@ -1025,8 +1011,6 @@ export const frameworks = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
dependency: 'sirv-cli',
|
dependency: 'sirv-cli',
|
||||||
devCommand: 'rollup -c -w',
|
|
||||||
buildCommand: 'rollup -c',
|
|
||||||
getFsOutputDir: async () => 'public',
|
getFsOutputDir: async () => 'public',
|
||||||
getOutputDirName: async () => 'public',
|
getOutputDirName: async () => 'public',
|
||||||
defaultRoutes: [
|
defaultRoutes: [
|
||||||
@@ -1070,6 +1054,7 @@ export const frameworks = [
|
|||||||
},
|
},
|
||||||
buildCommand: {
|
buildCommand: {
|
||||||
placeholder: '`npm run build` or `svelte-kit build`',
|
placeholder: '`npm run build` or `svelte-kit build`',
|
||||||
|
value: 'svelte-kit build',
|
||||||
},
|
},
|
||||||
devCommand: {
|
devCommand: {
|
||||||
value: 'svelte-kit dev --port $PORT',
|
value: 'svelte-kit dev --port $PORT',
|
||||||
@@ -1079,8 +1064,6 @@ export const frameworks = [
|
|||||||
placeholder: 'public',
|
placeholder: 'public',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
devCommand: 'svelte-kit dev --port $PORT',
|
|
||||||
buildCommand: 'svelte-kit build',
|
|
||||||
getFsOutputDir: async () => '.output',
|
getFsOutputDir: async () => '.output',
|
||||||
getOutputDirName: async () => 'public',
|
getOutputDirName: async () => 'public',
|
||||||
},
|
},
|
||||||
@@ -1108,6 +1091,7 @@ export const frameworks = [
|
|||||||
},
|
},
|
||||||
buildCommand: {
|
buildCommand: {
|
||||||
placeholder: '`npm run build` or `react-scripts build`',
|
placeholder: '`npm run build` or `react-scripts build`',
|
||||||
|
value: 'react-scripts build',
|
||||||
},
|
},
|
||||||
devCommand: {
|
devCommand: {
|
||||||
value: 'react-scripts start',
|
value: 'react-scripts start',
|
||||||
@@ -1117,8 +1101,6 @@ export const frameworks = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
dependency: '@ionic/react',
|
dependency: '@ionic/react',
|
||||||
devCommand: 'react-scripts start',
|
|
||||||
buildCommand: 'react-scripts build',
|
|
||||||
getFsOutputDir: async () => 'build',
|
getFsOutputDir: async () => 'build',
|
||||||
getOutputDirName: async () => 'build',
|
getOutputDirName: async () => 'build',
|
||||||
defaultRoutes: [
|
defaultRoutes: [
|
||||||
@@ -1216,6 +1198,7 @@ export const frameworks = [
|
|||||||
},
|
},
|
||||||
buildCommand: {
|
buildCommand: {
|
||||||
placeholder: '`npm run build` or `react-scripts build`',
|
placeholder: '`npm run build` or `react-scripts build`',
|
||||||
|
value: 'react-scripts build',
|
||||||
},
|
},
|
||||||
devCommand: {
|
devCommand: {
|
||||||
value: 'react-scripts start',
|
value: 'react-scripts start',
|
||||||
@@ -1225,8 +1208,6 @@ export const frameworks = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
dependency: 'react-scripts',
|
dependency: 'react-scripts',
|
||||||
devCommand: 'react-scripts start',
|
|
||||||
buildCommand: 'react-scripts build',
|
|
||||||
getFsOutputDir: async () => 'build',
|
getFsOutputDir: async () => 'build',
|
||||||
getOutputDirName: async () => 'build',
|
getOutputDirName: async () => 'build',
|
||||||
defaultRoutes: [
|
defaultRoutes: [
|
||||||
@@ -1318,6 +1299,7 @@ export const frameworks = [
|
|||||||
},
|
},
|
||||||
buildCommand: {
|
buildCommand: {
|
||||||
placeholder: '`npm run build` or `gridsome build`',
|
placeholder: '`npm run build` or `gridsome build`',
|
||||||
|
value: 'gridsome build',
|
||||||
},
|
},
|
||||||
devCommand: {
|
devCommand: {
|
||||||
value: 'gridsome develop -p $PORT',
|
value: 'gridsome develop -p $PORT',
|
||||||
@@ -1328,8 +1310,6 @@ export const frameworks = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
dependency: 'gridsome',
|
dependency: 'gridsome',
|
||||||
devCommand: 'gridsome develop -p $PORT',
|
|
||||||
buildCommand: 'gridsome build',
|
|
||||||
getFsOutputDir: async () => 'dist',
|
getFsOutputDir: async () => 'dist',
|
||||||
getOutputDirName: async () => 'dist',
|
getOutputDirName: async () => 'dist',
|
||||||
},
|
},
|
||||||
@@ -1357,6 +1337,7 @@ export const frameworks = [
|
|||||||
},
|
},
|
||||||
buildCommand: {
|
buildCommand: {
|
||||||
placeholder: '`npm run build` or `umi build`',
|
placeholder: '`npm run build` or `umi build`',
|
||||||
|
value: 'umi build',
|
||||||
},
|
},
|
||||||
devCommand: {
|
devCommand: {
|
||||||
value: 'umi dev --port $PORT',
|
value: 'umi dev --port $PORT',
|
||||||
@@ -1367,8 +1348,6 @@ export const frameworks = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
dependency: 'umi',
|
dependency: 'umi',
|
||||||
devCommand: 'umi dev --port $PORT',
|
|
||||||
buildCommand: 'umi build',
|
|
||||||
getFsOutputDir: async () => 'dist',
|
getFsOutputDir: async () => 'dist',
|
||||||
getOutputDirName: async () => 'dist',
|
getOutputDirName: async () => 'dist',
|
||||||
defaultRoutes: [
|
defaultRoutes: [
|
||||||
@@ -1412,6 +1391,7 @@ export const frameworks = [
|
|||||||
},
|
},
|
||||||
buildCommand: {
|
buildCommand: {
|
||||||
placeholder: '`npm run build` or `sapper export`',
|
placeholder: '`npm run build` or `sapper export`',
|
||||||
|
value: 'sapper export',
|
||||||
},
|
},
|
||||||
devCommand: {
|
devCommand: {
|
||||||
value: 'sapper dev --port $PORT',
|
value: 'sapper dev --port $PORT',
|
||||||
@@ -1422,8 +1402,6 @@ export const frameworks = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
dependency: 'sapper',
|
dependency: 'sapper',
|
||||||
devCommand: 'sapper dev --port $PORT',
|
|
||||||
buildCommand: 'sapper export',
|
|
||||||
getFsOutputDir: async () => '__sapper__/export',
|
getFsOutputDir: async () => '__sapper__/export',
|
||||||
getOutputDirName: async () => '__sapper__/export',
|
getOutputDirName: async () => '__sapper__/export',
|
||||||
},
|
},
|
||||||
@@ -1451,6 +1429,7 @@ export const frameworks = [
|
|||||||
},
|
},
|
||||||
buildCommand: {
|
buildCommand: {
|
||||||
placeholder: '`npm run build` or `saber build`',
|
placeholder: '`npm run build` or `saber build`',
|
||||||
|
value: 'saber build',
|
||||||
},
|
},
|
||||||
devCommand: {
|
devCommand: {
|
||||||
value: 'saber --port $PORT',
|
value: 'saber --port $PORT',
|
||||||
@@ -1461,8 +1440,6 @@ export const frameworks = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
dependency: 'saber',
|
dependency: 'saber',
|
||||||
devCommand: 'saber --port $PORT',
|
|
||||||
buildCommand: 'saber build',
|
|
||||||
getFsOutputDir: async () => 'public',
|
getFsOutputDir: async () => 'public',
|
||||||
getOutputDirName: async () => 'public',
|
getOutputDirName: async () => 'public',
|
||||||
defaultRoutes: [
|
defaultRoutes: [
|
||||||
@@ -1521,6 +1498,7 @@ export const frameworks = [
|
|||||||
},
|
},
|
||||||
buildCommand: {
|
buildCommand: {
|
||||||
placeholder: '`npm run build` or `stencil build`',
|
placeholder: '`npm run build` or `stencil build`',
|
||||||
|
value: 'stencil build',
|
||||||
},
|
},
|
||||||
devCommand: {
|
devCommand: {
|
||||||
value: 'stencil build --dev --watch --serve --port $PORT',
|
value: 'stencil build --dev --watch --serve --port $PORT',
|
||||||
@@ -1531,8 +1509,6 @@ export const frameworks = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
dependency: '@stencil/core',
|
dependency: '@stencil/core',
|
||||||
devCommand: 'stencil build --dev --watch --serve --port $PORT',
|
|
||||||
buildCommand: 'stencil build',
|
|
||||||
getFsOutputDir: async () => 'www',
|
getFsOutputDir: async () => 'www',
|
||||||
getOutputDirName: async () => 'www',
|
getOutputDirName: async () => 'www',
|
||||||
defaultRoutes: [
|
defaultRoutes: [
|
||||||
@@ -1611,6 +1587,7 @@ export const frameworks = [
|
|||||||
},
|
},
|
||||||
buildCommand: {
|
buildCommand: {
|
||||||
placeholder: '`npm run build` or `nuxt generate`',
|
placeholder: '`npm run build` or `nuxt generate`',
|
||||||
|
value: 'nuxt generate',
|
||||||
},
|
},
|
||||||
devCommand: {
|
devCommand: {
|
||||||
value: 'nuxt',
|
value: 'nuxt',
|
||||||
@@ -1620,8 +1597,6 @@ export const frameworks = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
dependency: 'nuxt',
|
dependency: 'nuxt',
|
||||||
devCommand: 'nuxt',
|
|
||||||
buildCommand: 'nuxt generate',
|
|
||||||
getFsOutputDir: async () => '.output',
|
getFsOutputDir: async () => '.output',
|
||||||
getOutputDirName: async () => 'dist',
|
getOutputDirName: async () => 'dist',
|
||||||
cachePattern: '.nuxt/**',
|
cachePattern: '.nuxt/**',
|
||||||
@@ -1680,8 +1655,6 @@ export const frameworks = [
|
|||||||
placeholder: 'RedwoodJS default',
|
placeholder: 'RedwoodJS default',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
devCommand: 'yarn rw dev --fwd="--port=$PORT --open=false',
|
|
||||||
buildCommand: 'yarn rw deploy vercel',
|
|
||||||
getFsOutputDir: async () => 'public',
|
getFsOutputDir: async () => 'public',
|
||||||
getOutputDirName: async () => 'public',
|
getOutputDirName: async () => 'public',
|
||||||
},
|
},
|
||||||
@@ -1717,6 +1690,7 @@ export const frameworks = [
|
|||||||
},
|
},
|
||||||
buildCommand: {
|
buildCommand: {
|
||||||
placeholder: '`npm run build` or `hugo -D --gc`',
|
placeholder: '`npm run build` or `hugo -D --gc`',
|
||||||
|
value: 'hugo -D --gc',
|
||||||
},
|
},
|
||||||
devCommand: {
|
devCommand: {
|
||||||
value: 'hugo server -D -w -p $PORT',
|
value: 'hugo server -D -w -p $PORT',
|
||||||
@@ -1726,8 +1700,6 @@ export const frameworks = [
|
|||||||
placeholder: '`public` or `publishDir` from the `config` file',
|
placeholder: '`public` or `publishDir` from the `config` file',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
devCommand: 'hugo server -D -w -p $PORT',
|
|
||||||
buildCommand: 'hugo -D --gc',
|
|
||||||
getFsOutputDir: async (dirPrefix: string): Promise<string> => {
|
getFsOutputDir: async (dirPrefix: string): Promise<string> => {
|
||||||
type HugoConfig = { publishDir?: string };
|
type HugoConfig = { publishDir?: string };
|
||||||
const config = await readConfigFile<HugoConfig>(
|
const config = await readConfigFile<HugoConfig>(
|
||||||
@@ -1772,6 +1744,7 @@ export const frameworks = [
|
|||||||
},
|
},
|
||||||
buildCommand: {
|
buildCommand: {
|
||||||
placeholder: '`npm run build` or `jekyll build`',
|
placeholder: '`npm run build` or `jekyll build`',
|
||||||
|
value: 'jekyll build',
|
||||||
},
|
},
|
||||||
devCommand: {
|
devCommand: {
|
||||||
value: 'bundle exec jekyll serve --watch --port $PORT',
|
value: 'bundle exec jekyll serve --watch --port $PORT',
|
||||||
@@ -1781,8 +1754,6 @@ export const frameworks = [
|
|||||||
placeholder: '`_site` or `destination` from `_config.yml`',
|
placeholder: '`_site` or `destination` from `_config.yml`',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
devCommand: 'bundle exec jekyll serve --watch --port $PORT',
|
|
||||||
buildCommand: 'jekyll build',
|
|
||||||
getFsOutputDir: async (dirPrefix: string): Promise<string> => {
|
getFsOutputDir: async (dirPrefix: string): Promise<string> => {
|
||||||
type JekyllConfig = { destination?: string };
|
type JekyllConfig = { destination?: string };
|
||||||
const config = await readConfigFile<JekyllConfig>(
|
const config = await readConfigFile<JekyllConfig>(
|
||||||
@@ -1821,6 +1792,7 @@ export const frameworks = [
|
|||||||
},
|
},
|
||||||
buildCommand: {
|
buildCommand: {
|
||||||
placeholder: '`npm run build` or `brunch build --production`',
|
placeholder: '`npm run build` or `brunch build --production`',
|
||||||
|
value: 'brunch build --production',
|
||||||
},
|
},
|
||||||
devCommand: {
|
devCommand: {
|
||||||
value: 'brunch watch --server --port $PORT',
|
value: 'brunch watch --server --port $PORT',
|
||||||
@@ -1830,8 +1802,6 @@ export const frameworks = [
|
|||||||
value: 'public',
|
value: 'public',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
devCommand: 'brunch watch --server --port $PORT',
|
|
||||||
buildCommand: 'brunch build --production',
|
|
||||||
getFsOutputDir: async () => 'public',
|
getFsOutputDir: async () => 'public',
|
||||||
getOutputDirName: async () => 'public',
|
getOutputDirName: async () => 'public',
|
||||||
},
|
},
|
||||||
@@ -1856,18 +1826,17 @@ export const frameworks = [
|
|||||||
value: 'bundle install',
|
value: 'bundle install',
|
||||||
},
|
},
|
||||||
buildCommand: {
|
buildCommand: {
|
||||||
value: '`npm run build` or `bundle exec middleman build`',
|
placeholder: '`npm run build` or `bundle exec middleman build`',
|
||||||
|
value: 'bundle exec middleman build',
|
||||||
},
|
},
|
||||||
devCommand: {
|
devCommand: {
|
||||||
value: 'bundle exec middleman server -p $PORT',
|
|
||||||
placeholder: 'bundle exec middleman server',
|
placeholder: 'bundle exec middleman server',
|
||||||
|
value: 'bundle exec middleman server -p $PORT',
|
||||||
},
|
},
|
||||||
outputDirectory: {
|
outputDirectory: {
|
||||||
value: 'build',
|
value: 'build',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
devCommand: 'bundle exec middleman server -p $PORT',
|
|
||||||
buildCommand: 'bundle exec middleman build',
|
|
||||||
getFsOutputDir: async () => 'build',
|
getFsOutputDir: async () => 'build',
|
||||||
getOutputDirName: async () => 'build',
|
getOutputDirName: async () => 'build',
|
||||||
cachePattern: '{vendor/bin,vendor/cache,vendor/bundle}/**',
|
cachePattern: '{vendor/bin,vendor/cache,vendor/bundle}/**',
|
||||||
@@ -1896,15 +1865,13 @@ export const frameworks = [
|
|||||||
value: 'zola build',
|
value: 'zola build',
|
||||||
},
|
},
|
||||||
devCommand: {
|
devCommand: {
|
||||||
value: 'zola serve --port $PORT',
|
|
||||||
placeholder: 'zola serve',
|
placeholder: 'zola serve',
|
||||||
|
value: 'zola serve --port $PORT',
|
||||||
},
|
},
|
||||||
outputDirectory: {
|
outputDirectory: {
|
||||||
value: 'public',
|
value: 'public',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
devCommand: 'zola serve --port $PORT',
|
|
||||||
buildCommand: 'zola build',
|
|
||||||
getFsOutputDir: async () => 'public',
|
getFsOutputDir: async () => 'public',
|
||||||
getOutputDirName: async () => 'public',
|
getOutputDirName: async () => 'public',
|
||||||
defaultVersion: '0.13.0',
|
defaultVersion: '0.13.0',
|
||||||
@@ -1934,17 +1901,17 @@ export const frameworks = [
|
|||||||
},
|
},
|
||||||
buildCommand: {
|
buildCommand: {
|
||||||
placeholder: '`npm run build` or `vite build`',
|
placeholder: '`npm run build` or `vite build`',
|
||||||
|
value: 'vite build',
|
||||||
},
|
},
|
||||||
devCommand: {
|
devCommand: {
|
||||||
placeholder: 'vite',
|
placeholder: 'vite',
|
||||||
|
value: 'vite',
|
||||||
},
|
},
|
||||||
outputDirectory: {
|
outputDirectory: {
|
||||||
value: 'dist',
|
value: 'dist',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
dependency: 'vite',
|
dependency: 'vite',
|
||||||
devCommand: 'vite',
|
|
||||||
buildCommand: 'vite build',
|
|
||||||
getFsOutputDir: async () => 'dist',
|
getFsOutputDir: async () => 'dist',
|
||||||
getOutputDirName: async () => 'dist',
|
getOutputDirName: async () => 'dist',
|
||||||
},
|
},
|
||||||
@@ -1972,17 +1939,17 @@ export const frameworks = [
|
|||||||
},
|
},
|
||||||
buildCommand: {
|
buildCommand: {
|
||||||
placeholder: '`npm run build` or `parcel build`',
|
placeholder: '`npm run build` or `parcel build`',
|
||||||
|
value: 'parcel build',
|
||||||
},
|
},
|
||||||
devCommand: {
|
devCommand: {
|
||||||
placeholder: 'parcel',
|
placeholder: 'parcel',
|
||||||
|
value: 'parcel',
|
||||||
},
|
},
|
||||||
outputDirectory: {
|
outputDirectory: {
|
||||||
placeholder: 'dist',
|
value: 'dist',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
dependency: 'parcel',
|
dependency: 'parcel',
|
||||||
devCommand: 'parcel',
|
|
||||||
buildCommand: 'parcel build',
|
|
||||||
getFsOutputDir: async () => 'dist',
|
getFsOutputDir: async () => 'dist',
|
||||||
getOutputDirName: async () => 'dist',
|
getOutputDirName: async () => 'dist',
|
||||||
defaultRoutes: [
|
defaultRoutes: [
|
||||||
@@ -2016,16 +1983,16 @@ export const frameworks = [
|
|||||||
},
|
},
|
||||||
buildCommand: {
|
buildCommand: {
|
||||||
placeholder: '`npm run vercel-build` or `npm run build`',
|
placeholder: '`npm run vercel-build` or `npm run build`',
|
||||||
|
value: null,
|
||||||
},
|
},
|
||||||
devCommand: {
|
devCommand: {
|
||||||
placeholder: 'None',
|
placeholder: 'None',
|
||||||
|
value: null,
|
||||||
},
|
},
|
||||||
outputDirectory: {
|
outputDirectory: {
|
||||||
placeholder: '`public` if it exists, or `.`',
|
placeholder: '`public` if it exists, or `.`',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
devCommand: null,
|
|
||||||
buildCommand: null,
|
|
||||||
getFsOutputDir: async (dirPrefix: string): Promise<string> => {
|
getFsOutputDir: async (dirPrefix: string): Promise<string> => {
|
||||||
// Public if it exists or `.`
|
// Public if it exists or `.`
|
||||||
let base = 'public';
|
let base = 'public';
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export interface SettingValue {
|
|||||||
* A predefined setting for the detected framework
|
* A predefined setting for the detected framework
|
||||||
* @example "next dev --port $PORT"
|
* @example "next dev --port $PORT"
|
||||||
*/
|
*/
|
||||||
value: string;
|
value: string | null;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,11 +129,11 @@ export interface Framework {
|
|||||||
/**
|
/**
|
||||||
* Default Build Command or a placeholder
|
* Default Build Command or a placeholder
|
||||||
*/
|
*/
|
||||||
buildCommand: Setting;
|
buildCommand: SettingValue;
|
||||||
/**
|
/**
|
||||||
* Default Development Command or a placeholder
|
* Default Development Command or a placeholder
|
||||||
*/
|
*/
|
||||||
devCommand: Setting;
|
devCommand: SettingValue;
|
||||||
/**
|
/**
|
||||||
* Default Output Directory
|
* Default Output Directory
|
||||||
*/
|
*/
|
||||||
@@ -157,6 +157,7 @@ export interface Framework {
|
|||||||
/**
|
/**
|
||||||
* Name of a dependency in `package.json` to detect this framework.
|
* Name of a dependency in `package.json` to detect this framework.
|
||||||
* @example "hexo"
|
* @example "hexo"
|
||||||
|
* @deprecated use `detectors` instead (new frameworks should not use this prop)
|
||||||
*/
|
*/
|
||||||
dependency?: string;
|
dependency?: string;
|
||||||
/**
|
/**
|
||||||
@@ -201,16 +202,6 @@ export interface Framework {
|
|||||||
* @example ".cache/**"
|
* @example ".cache/**"
|
||||||
*/
|
*/
|
||||||
cachePattern?: string;
|
cachePattern?: string;
|
||||||
/**
|
|
||||||
* The default build command for the framework.
|
|
||||||
* @example "next build"
|
|
||||||
*/
|
|
||||||
buildCommand: string | null;
|
|
||||||
/**
|
|
||||||
* The default development command for the framework.
|
|
||||||
* @example "next dev"
|
|
||||||
*/
|
|
||||||
devCommand: string | null;
|
|
||||||
/**
|
/**
|
||||||
* The default version of the framework command that is available within the
|
* The default version of the framework command that is available within the
|
||||||
* build image. Usually an environment variable can be set to override this.
|
* build image. Usually an environment variable can be set to override this.
|
||||||
|
|||||||
14
packages/frameworks/test/frameworks.unit.test.ts
vendored
14
packages/frameworks/test/frameworks.unit.test.ts
vendored
@@ -34,7 +34,7 @@ const SchemaSettings = {
|
|||||||
additionalProperties: false,
|
additionalProperties: false,
|
||||||
properties: {
|
properties: {
|
||||||
value: {
|
value: {
|
||||||
type: 'string',
|
type: ['string', 'null'],
|
||||||
},
|
},
|
||||||
placeholder: {
|
placeholder: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
@@ -58,15 +58,7 @@ const Schema = {
|
|||||||
type: 'array',
|
type: 'array',
|
||||||
items: {
|
items: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
required: [
|
required: ['name', 'slug', 'logo', 'description', 'settings'],
|
||||||
'name',
|
|
||||||
'slug',
|
|
||||||
'logo',
|
|
||||||
'description',
|
|
||||||
'settings',
|
|
||||||
'buildCommand',
|
|
||||||
'devCommand',
|
|
||||||
],
|
|
||||||
properties: {
|
properties: {
|
||||||
name: { type: 'string' },
|
name: { type: 'string' },
|
||||||
slug: { type: ['string', 'null'] },
|
slug: { type: ['string', 'null'] },
|
||||||
@@ -138,8 +130,6 @@ const Schema = {
|
|||||||
|
|
||||||
dependency: { type: 'string' },
|
dependency: { type: 'string' },
|
||||||
cachePattern: { type: 'string' },
|
cachePattern: { type: 'string' },
|
||||||
buildCommand: { type: ['string', 'null'] },
|
|
||||||
devCommand: { type: ['string', 'null'] },
|
|
||||||
defaultVersion: { type: 'string' },
|
defaultVersion: { type: 'string' },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ import { join } from 'path';
|
|||||||
import stringArgv from 'string-argv';
|
import stringArgv from 'string-argv';
|
||||||
import { debug } from '@vercel/build-utils';
|
import { debug } from '@vercel/build-utils';
|
||||||
const versionMap = new Map([
|
const versionMap = new Map([
|
||||||
['1.16', '1.16'],
|
['1.17', '1.17.3'],
|
||||||
|
['1.16', '1.16.10'],
|
||||||
['1.15', '1.15.8'],
|
['1.15', '1.15.8'],
|
||||||
['1.14', '1.14.15'],
|
['1.14', '1.14.15'],
|
||||||
['1.13', '1.13.15'],
|
['1.13', '1.13.15'],
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vercel/go",
|
"name": "@vercel/go",
|
||||||
"version": "1.2.4-canary.3",
|
"version": "1.2.4-canary.4",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"main": "./dist/index",
|
"main": "./dist/index",
|
||||||
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/go",
|
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/go",
|
||||||
|
|||||||
@@ -5,10 +5,10 @@
|
|||||||
{ "src": "subdirectory/index.go", "use": "@vercel/go" }
|
{ "src": "subdirectory/index.go", "use": "@vercel/go" }
|
||||||
],
|
],
|
||||||
"probes": [
|
"probes": [
|
||||||
{ "path": "/", "mustContain": "cow:go1.16:RANDOMNESS_PLACEHOLDER" },
|
{ "path": "/", "mustContain": "cow:go1.17.3:RANDOMNESS_PLACEHOLDER" },
|
||||||
{
|
{
|
||||||
"path": "/subdirectory",
|
"path": "/subdirectory",
|
||||||
"mustContain": "subcow:go1.16:RANDOMNESS_PLACEHOLDER"
|
"mustContain": "subcow:go1.17.3:RANDOMNESS_PLACEHOLDER"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
module with-nested
|
module with-nested
|
||||||
|
|
||||||
go 1.12
|
go 1.16
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "vercel-plugin-middleware",
|
"name": "vercel-plugin-middleware",
|
||||||
"version": "0.0.0-canary.6",
|
"version": "0.0.0-canary.7",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"main": "./dist/index",
|
"main": "./dist/index",
|
||||||
"homepage": "",
|
"homepage": "",
|
||||||
|
|||||||
@@ -3,13 +3,16 @@ _ENTRIES = typeof _ENTRIES === 'undefined' ? {} : _ENTRIES;
|
|||||||
_ENTRIES['middleware_pages/_middleware'] = {
|
_ENTRIES['middleware_pages/_middleware'] = {
|
||||||
default: async function (ev) {
|
default: async function (ev) {
|
||||||
const result = await middleware.default(ev.request, ev);
|
const result = await middleware.default(ev.request, ev);
|
||||||
if (result instanceof Response) {
|
return {
|
||||||
return {
|
promise: Promise.resolve(),
|
||||||
promise: Promise.resolve(),
|
waitUntil: Promise.resolve(),
|
||||||
waitUntil: Promise.resolve(),
|
response:
|
||||||
response: result,
|
result ||
|
||||||
};
|
new Response(null, {
|
||||||
}
|
headers: {
|
||||||
return result;
|
'x-middleware-next': 1,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
};
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
37
packages/middleware/test/build.test.ts
vendored
37
packages/middleware/test/build.test.ts
vendored
@@ -1,8 +1,19 @@
|
|||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
import { promises as fsp } from 'fs';
|
import { promises as fsp } from 'fs';
|
||||||
import { build } from '../src';
|
import { build } from '../src';
|
||||||
|
import { Response } from 'node-fetch';
|
||||||
|
|
||||||
describe('build()', () => {
|
describe('build()', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
//@ts-ignore
|
||||||
|
global.Response = Response;
|
||||||
|
});
|
||||||
|
afterEach(() => {
|
||||||
|
//@ts-ignore
|
||||||
|
delete global.Response;
|
||||||
|
//@ts-ignore
|
||||||
|
delete global._ENTRIES;
|
||||||
|
});
|
||||||
it('should build simple middleware', async () => {
|
it('should build simple middleware', async () => {
|
||||||
const fixture = join(__dirname, 'fixtures/simple');
|
const fixture = join(__dirname, 'fixtures/simple');
|
||||||
await build({
|
await build({
|
||||||
@@ -17,8 +28,30 @@ describe('build()', () => {
|
|||||||
);
|
);
|
||||||
expect(middlewareManifest).toMatchSnapshot();
|
expect(middlewareManifest).toMatchSnapshot();
|
||||||
|
|
||||||
|
const outputFile = join(fixture, '.output/server/pages/_middleware.js');
|
||||||
|
expect(await fsp.stat(outputFile)).toBeTruthy();
|
||||||
|
|
||||||
|
require(outputFile);
|
||||||
|
//@ts-ignore
|
||||||
|
const middleware = global._ENTRIES['middleware_pages/_middleware'].default;
|
||||||
|
expect(typeof middleware).toStrictEqual('function');
|
||||||
|
const handledResponse = await middleware({
|
||||||
|
request: {
|
||||||
|
url: 'http://google.com',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const unhandledResponse = await middleware({
|
||||||
|
request: {
|
||||||
|
url: 'literallyanythingelse',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(String(handledResponse.response.body)).toEqual('Hi from the edge!');
|
||||||
expect(
|
expect(
|
||||||
await fsp.stat(join(fixture, '.output/server/pages/_middleware.js'))
|
(handledResponse.response as Response).headers.get('x-middleware-next')
|
||||||
).toBeTruthy();
|
).toEqual(null);
|
||||||
|
expect(unhandledResponse.response.body).toEqual(null);
|
||||||
|
expect(
|
||||||
|
(unhandledResponse.response as Response).headers.get('x-middleware-next')
|
||||||
|
).toEqual('1');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1 +1,5 @@
|
|||||||
export default () => new Response('Hi from the edge!');
|
export default req => {
|
||||||
|
if (req.url === 'http://google.com') {
|
||||||
|
return new Response('Hi from the edge!');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
@@ -33,14 +33,6 @@ async function main() {
|
|||||||
await fs.remove(symlinkTarget);
|
await fs.remove(symlinkTarget);
|
||||||
await fs.symlink('symlinked-asset', symlinkTarget);
|
await fs.symlink('symlinked-asset', symlinkTarget);
|
||||||
|
|
||||||
// Use types.d.ts as the main types export
|
|
||||||
await Promise.all(
|
|
||||||
(await fs.readdir(outDir))
|
|
||||||
.filter(p => p.endsWith('.d.ts') && p !== 'types.d.ts')
|
|
||||||
.map(p => fs.remove(join(outDir, p)))
|
|
||||||
);
|
|
||||||
await fs.rename(join(outDir, 'types.d.ts'), join(outDir, 'index.d.ts'));
|
|
||||||
|
|
||||||
// Bundle helpers.ts with ncc
|
// Bundle helpers.ts with ncc
|
||||||
await fs.remove(join(outDir, 'helpers.js'));
|
await fs.remove(join(outDir, 'helpers.js'));
|
||||||
const helpersDir = join(outDir, 'helpers');
|
const helpersDir = join(outDir, 'helpers');
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vercel/node",
|
"name": "@vercel/node",
|
||||||
"version": "1.12.2-canary.6",
|
"version": "1.12.2-canary.7",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"main": "./dist/index",
|
"main": "./dist/index",
|
||||||
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/node-js",
|
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/node-js",
|
||||||
|
|||||||
@@ -339,6 +339,9 @@ function getAWSLambdaHandler(entrypoint: string, config: Config) {
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensures that everything from `types.ts` is exported in the final `index.d.ts` file.
|
||||||
|
export * from './types';
|
||||||
|
|
||||||
export const version = 3;
|
export const version = 3;
|
||||||
|
|
||||||
export async function build({
|
export async function build({
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"private": false,
|
"private": false,
|
||||||
"name": "vercel-plugin-go",
|
"name": "vercel-plugin-go",
|
||||||
"version": "1.0.0-canary.1",
|
"version": "1.0.0-canary.5",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"files": [
|
"files": [
|
||||||
@@ -17,8 +17,8 @@
|
|||||||
"prepublishOnly": "tsc"
|
"prepublishOnly": "tsc"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vercel/build-utils": "2.12.3-canary.17",
|
"@vercel/build-utils": "2.12.3-canary.20",
|
||||||
"@vercel/go": "1.2.4-canary.3"
|
"@vercel/go": "1.2.4-canary.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "*",
|
"@types/node": "*",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "vercel-plugin-node",
|
"name": "vercel-plugin-node",
|
||||||
"version": "1.12.2-canary.7",
|
"version": "1.12.2-canary.11",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"main": "./dist/index",
|
"main": "./dist/index",
|
||||||
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/node-js",
|
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/node-js",
|
||||||
@@ -33,7 +33,9 @@
|
|||||||
"@types/jest": "27.0.2",
|
"@types/jest": "27.0.2",
|
||||||
"@types/node-fetch": "2",
|
"@types/node-fetch": "2",
|
||||||
"@types/test-listen": "1.1.0",
|
"@types/test-listen": "1.1.0",
|
||||||
"@vercel/fun": "1.0.1",
|
"@types/yazl": "2.4.2",
|
||||||
|
"@vercel/build-utils": "2.12.3-canary.20",
|
||||||
|
"@vercel/fun": "1.0.3",
|
||||||
"@vercel/ncc": "0.24.0",
|
"@vercel/ncc": "0.24.0",
|
||||||
"@vercel/nft": "0.14.0",
|
"@vercel/nft": "0.14.0",
|
||||||
"@vercel/node-bridge": "2.1.1-canary.2",
|
"@vercel/node-bridge": "2.1.1-canary.2",
|
||||||
@@ -46,7 +48,8 @@
|
|||||||
"node-fetch": "2",
|
"node-fetch": "2",
|
||||||
"source-map-support": "0.5.12",
|
"source-map-support": "0.5.12",
|
||||||
"test-listen": "1.1.0",
|
"test-listen": "1.1.0",
|
||||||
"ts-morph": "12.0.0"
|
"ts-morph": "12.0.0",
|
||||||
|
"yazl": "2.5.1"
|
||||||
},
|
},
|
||||||
"jest": {
|
"jest": {
|
||||||
"preset": "ts-jest",
|
"preset": "ts-jest",
|
||||||
|
|||||||
@@ -35,7 +35,11 @@ import {
|
|||||||
debug,
|
debug,
|
||||||
isSymbolicLink,
|
isSymbolicLink,
|
||||||
runNpmInstall,
|
runNpmInstall,
|
||||||
|
updateFunctionsManifest,
|
||||||
|
updateRoutesManifest,
|
||||||
walkParentDirs,
|
walkParentDirs,
|
||||||
|
normalizePath,
|
||||||
|
runPackageJsonScript,
|
||||||
} from '@vercel/build-utils';
|
} from '@vercel/build-utils';
|
||||||
import { FromSchema } from 'json-schema-to-ts';
|
import { FromSchema } from 'json-schema-to-ts';
|
||||||
import { getConfig, BaseFunctionConfigSchema } from '@vercel/static-config';
|
import { getConfig, BaseFunctionConfigSchema } from '@vercel/static-config';
|
||||||
@@ -43,6 +47,7 @@ import { AbortController } from 'abort-controller';
|
|||||||
import { Register, register } from './typescript';
|
import { Register, register } from './typescript';
|
||||||
import { pageToRoute } from './router/page-to-route';
|
import { pageToRoute } from './router/page-to-route';
|
||||||
import { isDynamicRoute } from './router/is-dynamic';
|
import { isDynamicRoute } from './router/is-dynamic';
|
||||||
|
import crypto from 'crypto';
|
||||||
|
|
||||||
export { shouldServe };
|
export { shouldServe };
|
||||||
export {
|
export {
|
||||||
@@ -64,6 +69,7 @@ const { makeVercelLauncher, makeAwsLauncher } = require_(
|
|||||||
interface DownloadOptions {
|
interface DownloadOptions {
|
||||||
entrypoint: string;
|
entrypoint: string;
|
||||||
workPath: string;
|
workPath: string;
|
||||||
|
installedPaths?: Set<string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PortInfo {
|
interface PortInfo {
|
||||||
@@ -109,17 +115,47 @@ const BRIDGE_FILENAME = '__bridge.js';
|
|||||||
const HELPERS_FILENAME = '__helpers.js';
|
const HELPERS_FILENAME = '__helpers.js';
|
||||||
const SOURCEMAP_SUPPORT_FILENAME = '__sourcemap_support.js';
|
const SOURCEMAP_SUPPORT_FILENAME = '__sourcemap_support.js';
|
||||||
|
|
||||||
async function downloadInstallAndBundle({
|
async function maybeInstallAndBuild({
|
||||||
entrypoint,
|
entrypoint,
|
||||||
workPath,
|
workPath,
|
||||||
|
installedPaths,
|
||||||
}: DownloadOptions) {
|
}: DownloadOptions) {
|
||||||
const entrypointFsDirname = join(workPath, dirname(entrypoint));
|
const entrypointFsDirname = join(workPath, dirname(entrypoint));
|
||||||
const nodeVersion = await getNodeVersion(entrypointFsDirname);
|
const nodeVersion = await getNodeVersion(entrypointFsDirname);
|
||||||
const spawnOpts = getSpawnOptions({}, nodeVersion);
|
const spawnOpts = getSpawnOptions({}, nodeVersion);
|
||||||
|
|
||||||
const installTime = Date.now();
|
const lastPath = await walkParentDirs({
|
||||||
await runNpmInstall(entrypointFsDirname, [], spawnOpts, {}, nodeVersion);
|
base: workPath,
|
||||||
debug(`Install complete [${Date.now() - installTime}ms]`);
|
start: entrypointFsDirname,
|
||||||
|
filename: 'package.json',
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!lastPath || dirname(lastPath) === workPath) {
|
||||||
|
debug(`Skip install command in \`vercel-plugin-node\` for ${entrypoint}.`);
|
||||||
|
} else if (lastPath) {
|
||||||
|
if (!installedPaths?.has(lastPath)) {
|
||||||
|
installedPaths?.add(lastPath);
|
||||||
|
const installTime = Date.now();
|
||||||
|
await runNpmInstall(dirname(lastPath), [], spawnOpts, {}, nodeVersion);
|
||||||
|
debug(
|
||||||
|
`Install complete [${Date.now() - installTime}ms] for ${relative(
|
||||||
|
workPath,
|
||||||
|
lastPath
|
||||||
|
)}`
|
||||||
|
);
|
||||||
|
|
||||||
|
await runPackageJsonScript(
|
||||||
|
dirname(lastPath),
|
||||||
|
// Don't consider "build" script since its intended for frontend code
|
||||||
|
['vercel-build', 'now-build'],
|
||||||
|
spawnOpts
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
debug(
|
||||||
|
`Skip install command in \`vercel-plugin-node\` for ${entrypoint}. Already installed for other entrypoint.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
nodeVersion,
|
nodeVersion,
|
||||||
@@ -346,6 +382,7 @@ function getAWSLambdaHandler(entrypoint: string, config: FunctionConfig) {
|
|||||||
export async function build({ workPath }: { workPath: string }) {
|
export async function build({ workPath }: { workPath: string }) {
|
||||||
const project = new Project();
|
const project = new Project();
|
||||||
const entrypoints = await glob('api/**/*.[jt]s', workPath);
|
const entrypoints = await glob('api/**/*.[jt]s', workPath);
|
||||||
|
const installedPaths = new Set<string>();
|
||||||
for (const entrypoint of Object.keys(entrypoints)) {
|
for (const entrypoint of Object.keys(entrypoints)) {
|
||||||
// Dotfiles are not compiled
|
// Dotfiles are not compiled
|
||||||
if (entrypoint.includes('/.')) continue;
|
if (entrypoint.includes('/.')) continue;
|
||||||
@@ -370,7 +407,7 @@ export async function build({ workPath }: { workPath: string }) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
await buildEntrypoint({ workPath, entrypoint, config });
|
await buildEntrypoint({ workPath, entrypoint, config, installedPaths });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -378,46 +415,43 @@ export async function buildEntrypoint({
|
|||||||
workPath,
|
workPath,
|
||||||
entrypoint,
|
entrypoint,
|
||||||
config,
|
config,
|
||||||
|
installedPaths,
|
||||||
}: {
|
}: {
|
||||||
workPath: string;
|
workPath: string;
|
||||||
entrypoint: string;
|
entrypoint: string;
|
||||||
config: FunctionConfig;
|
config: FunctionConfig;
|
||||||
|
installedPaths?: Set<string>;
|
||||||
}) {
|
}) {
|
||||||
|
// Unique hash that will be used as directory name for `.output`.
|
||||||
|
const entrypointHash = crypto
|
||||||
|
.createHash('sha256')
|
||||||
|
.update(entrypoint)
|
||||||
|
.digest('hex');
|
||||||
const outputDirPath = join(workPath, '.output');
|
const outputDirPath = join(workPath, '.output');
|
||||||
|
|
||||||
const { dir, name } = parsePath(entrypoint);
|
const { dir, name } = parsePath(entrypoint);
|
||||||
const entrypointWithoutExt = join('/', dir, name);
|
const entrypointWithoutExt = join('/', dir, name);
|
||||||
const entrypointWithoutExtIndex = join(
|
const outputWorkPath = join(outputDirPath, 'inputs', entrypointHash);
|
||||||
dir,
|
const pagesDir = join(outputDirPath, 'server', 'pages');
|
||||||
name,
|
const pageOutput = join(pagesDir, renameTStoJS(entrypoint));
|
||||||
name === 'index' ? '' : 'index'
|
const nftOutput = `${pageOutput}.nft.json`;
|
||||||
);
|
|
||||||
const outputWorkPath = join(
|
|
||||||
outputDirPath,
|
|
||||||
'server/pages',
|
|
||||||
entrypointWithoutExtIndex
|
|
||||||
);
|
|
||||||
await fsp.mkdir(outputWorkPath, { recursive: true });
|
await fsp.mkdir(outputWorkPath, { recursive: true });
|
||||||
|
await fsp.mkdir(parsePath(pageOutput).dir, { recursive: true });
|
||||||
|
|
||||||
console.log(`Compiling "${entrypoint}" to "${outputWorkPath}"`);
|
console.log(`Compiling "${entrypoint}" to "${outputWorkPath}"`);
|
||||||
|
|
||||||
const shouldAddHelpers =
|
const shouldAddHelpers =
|
||||||
config.helpers !== false && process.env.NODEJS_HELPERS !== '0';
|
config.helpers !== false && process.env.NODEJS_HELPERS !== '0';
|
||||||
const awsLambdaHandler = getAWSLambdaHandler(entrypoint, config);
|
const awsLambdaHandler = getAWSLambdaHandler(entrypoint, config);
|
||||||
|
|
||||||
const { nodeVersion } = await downloadInstallAndBundle({
|
const { nodeVersion } = await maybeInstallAndBuild({
|
||||||
entrypoint,
|
entrypoint,
|
||||||
workPath,
|
workPath,
|
||||||
|
installedPaths,
|
||||||
});
|
});
|
||||||
const entrypointPath = join(workPath, entrypoint);
|
const entrypointPath = join(workPath, entrypoint);
|
||||||
|
|
||||||
// TODO NATE: do we want to run the build script?
|
|
||||||
// The frontend build command probably already did this
|
|
||||||
//await runPackageJsonScript(
|
|
||||||
// entrypointFsDirname,
|
|
||||||
// // Don't consider "build" script since its intended for frontend code
|
|
||||||
// ['vercel-build', 'now-build'],
|
|
||||||
// spawnOpts
|
|
||||||
//);
|
|
||||||
|
|
||||||
debug('Tracing input files...');
|
debug('Tracing input files...');
|
||||||
const traceTime = Date.now();
|
const traceTime = Date.now();
|
||||||
const { preparedFiles, shouldAddSourcemapSupport } = await compile(
|
const { preparedFiles, shouldAddSourcemapSupport } = await compile(
|
||||||
@@ -427,11 +461,12 @@ export async function buildEntrypoint({
|
|||||||
);
|
);
|
||||||
debug(`Trace complete [${Date.now() - traceTime}ms]`);
|
debug(`Trace complete [${Date.now() - traceTime}ms]`);
|
||||||
|
|
||||||
const getFileName = (str: string) => `___vc/${str}`;
|
// Has to be in `dirname(entrypoint)` because the `handler` will be prefixed with this path.
|
||||||
|
const getVCFileName = (str: string) => `${dirname(entrypoint)}/___vc/${str}`;
|
||||||
|
|
||||||
const launcher = awsLambdaHandler ? makeAwsLauncher : makeVercelLauncher;
|
const launcher = awsLambdaHandler ? makeAwsLauncher : makeVercelLauncher;
|
||||||
const launcherSource = launcher({
|
const launcherSource = launcher({
|
||||||
entrypointPath: `../${renameTStoJS(relative(workPath, entrypointPath))}`,
|
entrypointPath: `../${renameTStoJS(basename(entrypoint))}`,
|
||||||
bridgePath: `./${BRIDGE_FILENAME}`,
|
bridgePath: `./${BRIDGE_FILENAME}`,
|
||||||
helpersPath: `./${HELPERS_FILENAME}`,
|
helpersPath: `./${HELPERS_FILENAME}`,
|
||||||
sourcemapSupportPath: `./${SOURCEMAP_SUPPORT_FILENAME}`,
|
sourcemapSupportPath: `./${SOURCEMAP_SUPPORT_FILENAME}`,
|
||||||
@@ -441,25 +476,25 @@ export async function buildEntrypoint({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const launcherFiles: Files = {
|
const launcherFiles: Files = {
|
||||||
[getFileName('package.json')]: new FileBlob({
|
[getVCFileName('package.json')]: new FileBlob({
|
||||||
data: JSON.stringify({ type: 'commonjs' }),
|
data: JSON.stringify({ type: 'commonjs' }),
|
||||||
}),
|
}),
|
||||||
[getFileName(LAUNCHER_FILENAME)]: new FileBlob({
|
[getVCFileName(LAUNCHER_FILENAME)]: new FileBlob({
|
||||||
data: launcherSource,
|
data: launcherSource,
|
||||||
}),
|
}),
|
||||||
[getFileName(BRIDGE_FILENAME)]: new FileFsRef({
|
[getVCFileName(BRIDGE_FILENAME)]: new FileFsRef({
|
||||||
fsPath: join(DIST_DIR, 'bridge.js'),
|
fsPath: join(DIST_DIR, 'bridge.js'),
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (shouldAddSourcemapSupport) {
|
if (shouldAddSourcemapSupport) {
|
||||||
launcherFiles[getFileName(SOURCEMAP_SUPPORT_FILENAME)] = new FileFsRef({
|
launcherFiles[getVCFileName(SOURCEMAP_SUPPORT_FILENAME)] = new FileFsRef({
|
||||||
fsPath: join(DIST_DIR, 'source-map-support.js'),
|
fsPath: join(DIST_DIR, 'source-map-support.js'),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shouldAddHelpers) {
|
if (shouldAddHelpers) {
|
||||||
launcherFiles[getFileName(HELPERS_FILENAME)] = new FileFsRef({
|
launcherFiles[getVCFileName(HELPERS_FILENAME)] = new FileFsRef({
|
||||||
fsPath: join(DIST_DIR, 'helpers.js'),
|
fsPath: join(DIST_DIR, 'helpers.js'),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -470,6 +505,8 @@ export async function buildEntrypoint({
|
|||||||
...launcherFiles,
|
...launcherFiles,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const nftFiles: { input: string; output: string }[] = [];
|
||||||
|
|
||||||
for (const filename of Object.keys(files)) {
|
for (const filename of Object.keys(files)) {
|
||||||
const outPath = join(outputWorkPath, filename);
|
const outPath = join(outputWorkPath, filename);
|
||||||
const file = files[filename];
|
const file = files[filename];
|
||||||
@@ -480,48 +517,44 @@ export async function buildEntrypoint({
|
|||||||
const finishPromise = once(ws, 'finish');
|
const finishPromise = once(ws, 'finish');
|
||||||
file.toStream().pipe(ws);
|
file.toStream().pipe(ws);
|
||||||
await finishPromise;
|
await finishPromise;
|
||||||
|
|
||||||
|
// The `handler` will be `.output/server/pages/api/subdirectory/___vc/__launcher.launcher`
|
||||||
|
// or `.output/server/pages/api/___vc/__launcher.launcher`.
|
||||||
|
// This means everything has to be mounted to the `dirname` of the entrypoint.
|
||||||
|
nftFiles.push({
|
||||||
|
input: relative(dirname(nftOutput), outPath),
|
||||||
|
output: join('.output', 'server', 'pages', filename),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the `functions-mainifest.json` file with this entrypoint
|
|
||||||
// TODO NATE: turn this into a `@vercel/plugin-utils` helper function?
|
|
||||||
const functionsManifestPath = join(outputDirPath, 'functions-manifest.json');
|
|
||||||
let functionsManifest: any = {};
|
|
||||||
try {
|
|
||||||
functionsManifest = JSON.parse(
|
|
||||||
await fsp.readFile(functionsManifestPath, 'utf8')
|
|
||||||
);
|
|
||||||
} catch (_err) {
|
|
||||||
// ignore...
|
|
||||||
}
|
|
||||||
if (!functionsManifest.version) functionsManifest.version = 1;
|
|
||||||
if (!functionsManifest.pages) functionsManifest.pages = {};
|
|
||||||
functionsManifest.pages[entrypointWithoutExtIndex] = {
|
|
||||||
handler: `${getFileName(LAUNCHER_FILENAME).slice(0, -3)}.launcher`,
|
|
||||||
runtime: nodeVersion.runtime,
|
|
||||||
};
|
|
||||||
await fsp.writeFile(
|
await fsp.writeFile(
|
||||||
functionsManifestPath,
|
nftOutput,
|
||||||
JSON.stringify(functionsManifest, null, 2)
|
JSON.stringify({
|
||||||
|
version: 1,
|
||||||
|
files: nftFiles,
|
||||||
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await fsp.copyFile(
|
||||||
|
join(outputWorkPath, renameTStoJS(entrypoint)),
|
||||||
|
pageOutput
|
||||||
|
);
|
||||||
|
|
||||||
|
const pages = {
|
||||||
|
[normalizePath(relative(pagesDir, pageOutput))]: {
|
||||||
|
handler: `___vc/${LAUNCHER_FILENAME.slice(0, -3)}.launcher`,
|
||||||
|
runtime: nodeVersion.runtime,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
await updateFunctionsManifest({ workPath, pages });
|
||||||
|
|
||||||
// Update the `routes-mainifest.json` file with the wildcard route
|
// Update the `routes-mainifest.json` file with the wildcard route
|
||||||
// when the entrypoint is dynamic (i.e. `/api/[id].ts`).
|
// when the entrypoint is dynamic (i.e. `/api/[id].ts`).
|
||||||
if (isDynamicRoute(entrypointWithoutExt)) {
|
if (isDynamicRoute(entrypointWithoutExt)) {
|
||||||
const routesManifestPath = join(outputDirPath, 'routes-manifest.json');
|
await updateRoutesManifest({
|
||||||
let routesManifest: any = {};
|
workPath,
|
||||||
try {
|
dynamicRoutes: [pageToRoute(entrypointWithoutExt)],
|
||||||
routesManifest = JSON.parse(
|
});
|
||||||
await fsp.readFile(routesManifestPath, 'utf8')
|
|
||||||
);
|
|
||||||
} catch (_err) {
|
|
||||||
// ignore...
|
|
||||||
}
|
|
||||||
if (!routesManifest.dynamicRoutes) routesManifest.dynamicRoutes = [];
|
|
||||||
routesManifest.dynamicRoutes.push(pageToRoute(entrypointWithoutExt));
|
|
||||||
await fsp.writeFile(
|
|
||||||
routesManifestPath,
|
|
||||||
JSON.stringify(routesManifest, null, 2)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
84
packages/plugin-node/test/build.test.ts
vendored
84
packages/plugin-node/test/build.test.ts
vendored
@@ -1,6 +1,7 @@
|
|||||||
import { join } from 'path';
|
import path from 'path';
|
||||||
import { parse } from 'url';
|
import { parse } from 'url';
|
||||||
import { promises as fsp } from 'fs';
|
import { promises as fsp } from 'fs';
|
||||||
|
import { ZipFile } from 'yazl';
|
||||||
import { createFunction, Lambda } from '@vercel/fun';
|
import { createFunction, Lambda } from '@vercel/fun';
|
||||||
import {
|
import {
|
||||||
Request,
|
Request,
|
||||||
@@ -11,6 +12,7 @@ import {
|
|||||||
Headers,
|
Headers,
|
||||||
} from 'node-fetch';
|
} from 'node-fetch';
|
||||||
import { build } from '../src';
|
import { build } from '../src';
|
||||||
|
import { runNpmInstall, streamToBuffer } from '@vercel/build-utils';
|
||||||
|
|
||||||
interface TestParams {
|
interface TestParams {
|
||||||
fixture: string;
|
fixture: string;
|
||||||
@@ -50,40 +52,66 @@ function withFixture<T>(
|
|||||||
t: (props: TestParams) => Promise<T>
|
t: (props: TestParams) => Promise<T>
|
||||||
): () => Promise<T> {
|
): () => Promise<T> {
|
||||||
return async () => {
|
return async () => {
|
||||||
const fixture = join(__dirname, 'fixtures', name);
|
const fixture = path.join(__dirname, 'fixtures', name);
|
||||||
|
await fsp.rmdir(path.join(fixture, '.output'), { recursive: true });
|
||||||
|
|
||||||
const functions = new Map<string, Lambda>();
|
const functions = new Map<string, Lambda>();
|
||||||
|
|
||||||
async function fetch(r: RequestInfo, init?: RequestInit) {
|
async function fetch(r: RequestInfo, init?: RequestInit) {
|
||||||
const req = new Request(r, init);
|
const req = new Request(r, init);
|
||||||
const url = parse(req.url);
|
const url = parse(req.url);
|
||||||
const pathWithIndex = join(
|
const functionPath = url.pathname!.substring(1);
|
||||||
url.pathname!,
|
|
||||||
url.pathname!.endsWith('/index') ? '' : 'index'
|
|
||||||
).substring(1);
|
|
||||||
|
|
||||||
let status = 404;
|
let status = 404;
|
||||||
let headers: HeadersInit = {};
|
let headers: HeadersInit = {};
|
||||||
let body: string | Buffer = 'Function not found';
|
let body: string | Buffer = 'Function not found';
|
||||||
|
|
||||||
let fn = functions.get(pathWithIndex);
|
let fn = functions.get(functionPath);
|
||||||
if (!fn) {
|
if (!fn) {
|
||||||
const manifest = JSON.parse(
|
const manifest = JSON.parse(
|
||||||
await fsp.readFile(
|
await fsp.readFile(
|
||||||
join(fixture, '.output/functions-manifest.json'),
|
path.join(fixture, '.output/functions-manifest.json'),
|
||||||
'utf8'
|
'utf8'
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
const functionManifest = manifest.pages[pathWithIndex];
|
|
||||||
|
const keyFile = `${functionPath}.js`;
|
||||||
|
const keyIndex = `${functionPath}/index.js`;
|
||||||
|
const fnKey = keyFile in manifest.pages ? keyFile : keyIndex;
|
||||||
|
const functionManifest = manifest.pages[fnKey];
|
||||||
|
|
||||||
if (functionManifest) {
|
if (functionManifest) {
|
||||||
const dir = join(fixture, '.output/server/pages', pathWithIndex);
|
const entry = path.join(fixture, '.output/server/pages', fnKey);
|
||||||
|
const nftFile = JSON.parse(
|
||||||
|
await fsp.readFile(`${entry}.nft.json`, 'utf8')
|
||||||
|
);
|
||||||
|
|
||||||
|
const zip = new ZipFile();
|
||||||
|
zip.addFile(
|
||||||
|
path.join(fixture, '.output/server/pages', fnKey),
|
||||||
|
path.join('.output/server/pages', fnKey)
|
||||||
|
);
|
||||||
|
|
||||||
|
nftFile.files.forEach((f: { input: string; output: string }) => {
|
||||||
|
const input = path.join(path.dirname(entry), f.input);
|
||||||
|
zip.addFile(input, f.output);
|
||||||
|
});
|
||||||
|
zip.end();
|
||||||
|
|
||||||
|
const handler = path.posix.join(
|
||||||
|
'.output/server/pages',
|
||||||
|
path.dirname(fnKey),
|
||||||
|
functionManifest.handler
|
||||||
|
);
|
||||||
|
|
||||||
fn = await createFunction({
|
fn = await createFunction({
|
||||||
Code: {
|
Code: {
|
||||||
Directory: dir,
|
ZipFile: await streamToBuffer(zip.outputStream),
|
||||||
},
|
},
|
||||||
Handler: functionManifest.handler,
|
Handler: handler,
|
||||||
Runtime: functionManifest.runtime,
|
Runtime: functionManifest.runtime,
|
||||||
});
|
});
|
||||||
functions.set(pathWithIndex, fn);
|
functions.set(functionPath, fn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,6 +137,12 @@ function withFixture<T>(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
await fsp.lstat(path.join(fixture, 'package.json')).catch(() => false)
|
||||||
|
) {
|
||||||
|
await runNpmInstall(fixture);
|
||||||
|
}
|
||||||
|
|
||||||
await build({ workPath: fixture });
|
await build({ workPath: fixture });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -316,4 +350,28 @@ describe('build()', () => {
|
|||||||
expect(body3.includes('GraphQL Playground')).toEqual(true);
|
expect(body3.includes('GraphQL Playground')).toEqual(true);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
it(
|
||||||
|
'should build "nested-lock-and-build"',
|
||||||
|
withFixture('nested-lock-and-build', async ({ fetch }) => {
|
||||||
|
const resA = await fetch('/api/users/[id]');
|
||||||
|
|
||||||
|
expect(resA.headers.get('x-date')).toEqual('2021-11-20T20:00:00.000Z');
|
||||||
|
|
||||||
|
const body = await resA.text();
|
||||||
|
expect(body).toEqual(
|
||||||
|
' _______________________________\n' +
|
||||||
|
'< Hello from /api/users/[id].js >\n' +
|
||||||
|
' -------------------------------\n' +
|
||||||
|
' \\ ^__^\n' +
|
||||||
|
' \\ (oo)\\_______\n' +
|
||||||
|
' (__)\\ )\\/\\\n' +
|
||||||
|
' ||----w |\n' +
|
||||||
|
' || ||'
|
||||||
|
);
|
||||||
|
|
||||||
|
const resB = await fetch('/api/profile');
|
||||||
|
expect(await resB.text()).toEqual('true');
|
||||||
|
})
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
8
packages/plugin-node/test/fixtures/nested-lock-and-build/api/package.json
vendored
Normal file
8
packages/plugin-node/test/fixtures/nested-lock-and-build/api/package.json
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"scripts": {
|
||||||
|
"vercel-build": "node ./script.js"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"cowsay": "1.5.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
9
packages/plugin-node/test/fixtures/nested-lock-and-build/api/profile.js
vendored
Normal file
9
packages/plugin-node/test/fixtures/nested-lock-and-build/api/profile.js
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
const isSvg = require('is-svg');
|
||||||
|
|
||||||
|
module.exports = (req, res) => {
|
||||||
|
res.end(
|
||||||
|
isSvg(
|
||||||
|
'<svg xmlns="http://www.w3.org/2000/svg"><path fill="#00CD9F"/></svg>'
|
||||||
|
).toString()
|
||||||
|
);
|
||||||
|
};
|
||||||
11
packages/plugin-node/test/fixtures/nested-lock-and-build/api/script.js
vendored
Normal file
11
packages/plugin-node/test/fixtures/nested-lock-and-build/api/script.js
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(__dirname, 'users', '.date.js'),
|
||||||
|
`
|
||||||
|
module.exports = {
|
||||||
|
date: '2021-11-20T20:00:00.000Z'
|
||||||
|
};
|
||||||
|
`.trim()
|
||||||
|
);
|
||||||
11
packages/plugin-node/test/fixtures/nested-lock-and-build/api/users/[id].js
vendored
Normal file
11
packages/plugin-node/test/fixtures/nested-lock-and-build/api/users/[id].js
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
const cowsay = require('cowsay');
|
||||||
|
const { date } = require('./.date');
|
||||||
|
|
||||||
|
module.exports = (req, res) => {
|
||||||
|
res.setHeader('x-date', date);
|
||||||
|
res.end(
|
||||||
|
cowsay.say({
|
||||||
|
text: 'Hello from /api/users/[id].js',
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
220
packages/plugin-node/test/fixtures/nested-lock-and-build/api/yarn.lock
vendored
Normal file
220
packages/plugin-node/test/fixtures/nested-lock-and-build/api/yarn.lock
vendored
Normal file
@@ -0,0 +1,220 @@
|
|||||||
|
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||||
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
|
ansi-regex@^3.0.0:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998"
|
||||||
|
integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=
|
||||||
|
|
||||||
|
ansi-regex@^5.0.1:
|
||||||
|
version "5.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
|
||||||
|
integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
|
||||||
|
|
||||||
|
ansi-styles@^4.0.0:
|
||||||
|
version "4.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
|
||||||
|
integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
|
||||||
|
dependencies:
|
||||||
|
color-convert "^2.0.1"
|
||||||
|
|
||||||
|
camelcase@^5.0.0:
|
||||||
|
version "5.3.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
|
||||||
|
integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
|
||||||
|
|
||||||
|
cliui@^6.0.0:
|
||||||
|
version "6.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1"
|
||||||
|
integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==
|
||||||
|
dependencies:
|
||||||
|
string-width "^4.2.0"
|
||||||
|
strip-ansi "^6.0.0"
|
||||||
|
wrap-ansi "^6.2.0"
|
||||||
|
|
||||||
|
color-convert@^2.0.1:
|
||||||
|
version "2.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
|
||||||
|
integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
|
||||||
|
dependencies:
|
||||||
|
color-name "~1.1.4"
|
||||||
|
|
||||||
|
color-name@~1.1.4:
|
||||||
|
version "1.1.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
|
||||||
|
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
|
||||||
|
|
||||||
|
cowsay@1.5.0:
|
||||||
|
version "1.5.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/cowsay/-/cowsay-1.5.0.tgz#4a2a453b8b59383c7d7a50e44d765c5de0bf615f"
|
||||||
|
integrity sha512-8Ipzr54Z8zROr/62C8f0PdhQcDusS05gKTS87xxdji8VbWefWly0k8BwGK7+VqamOrkv3eGsCkPtvlHzrhWsCA==
|
||||||
|
dependencies:
|
||||||
|
get-stdin "8.0.0"
|
||||||
|
string-width "~2.1.1"
|
||||||
|
strip-final-newline "2.0.0"
|
||||||
|
yargs "15.4.1"
|
||||||
|
|
||||||
|
decamelize@^1.2.0:
|
||||||
|
version "1.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
|
||||||
|
integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=
|
||||||
|
|
||||||
|
emoji-regex@^8.0.0:
|
||||||
|
version "8.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
|
||||||
|
integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
|
||||||
|
|
||||||
|
find-up@^4.1.0:
|
||||||
|
version "4.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19"
|
||||||
|
integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==
|
||||||
|
dependencies:
|
||||||
|
locate-path "^5.0.0"
|
||||||
|
path-exists "^4.0.0"
|
||||||
|
|
||||||
|
get-caller-file@^2.0.1:
|
||||||
|
version "2.0.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
|
||||||
|
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
|
||||||
|
|
||||||
|
get-stdin@8.0.0:
|
||||||
|
version "8.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-8.0.0.tgz#cbad6a73feb75f6eeb22ba9e01f89aa28aa97a53"
|
||||||
|
integrity sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==
|
||||||
|
|
||||||
|
is-fullwidth-code-point@^2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
|
||||||
|
integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=
|
||||||
|
|
||||||
|
is-fullwidth-code-point@^3.0.0:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d"
|
||||||
|
integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
|
||||||
|
|
||||||
|
locate-path@^5.0.0:
|
||||||
|
version "5.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0"
|
||||||
|
integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==
|
||||||
|
dependencies:
|
||||||
|
p-locate "^4.1.0"
|
||||||
|
|
||||||
|
p-limit@^2.2.0:
|
||||||
|
version "2.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1"
|
||||||
|
integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==
|
||||||
|
dependencies:
|
||||||
|
p-try "^2.0.0"
|
||||||
|
|
||||||
|
p-locate@^4.1.0:
|
||||||
|
version "4.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07"
|
||||||
|
integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==
|
||||||
|
dependencies:
|
||||||
|
p-limit "^2.2.0"
|
||||||
|
|
||||||
|
p-try@^2.0.0:
|
||||||
|
version "2.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
|
||||||
|
integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
|
||||||
|
|
||||||
|
path-exists@^4.0.0:
|
||||||
|
version "4.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
|
||||||
|
integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==
|
||||||
|
|
||||||
|
require-directory@^2.1.1:
|
||||||
|
version "2.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
|
||||||
|
integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I=
|
||||||
|
|
||||||
|
require-main-filename@^2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b"
|
||||||
|
integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==
|
||||||
|
|
||||||
|
set-blocking@^2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
|
||||||
|
integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc=
|
||||||
|
|
||||||
|
string-width@^4.1.0, string-width@^4.2.0:
|
||||||
|
version "4.2.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
||||||
|
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
||||||
|
dependencies:
|
||||||
|
emoji-regex "^8.0.0"
|
||||||
|
is-fullwidth-code-point "^3.0.0"
|
||||||
|
strip-ansi "^6.0.1"
|
||||||
|
|
||||||
|
string-width@~2.1.1:
|
||||||
|
version "2.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e"
|
||||||
|
integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==
|
||||||
|
dependencies:
|
||||||
|
is-fullwidth-code-point "^2.0.0"
|
||||||
|
strip-ansi "^4.0.0"
|
||||||
|
|
||||||
|
strip-ansi@^4.0.0:
|
||||||
|
version "4.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f"
|
||||||
|
integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8=
|
||||||
|
dependencies:
|
||||||
|
ansi-regex "^3.0.0"
|
||||||
|
|
||||||
|
strip-ansi@^6.0.0, strip-ansi@^6.0.1:
|
||||||
|
version "6.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
|
||||||
|
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
|
||||||
|
dependencies:
|
||||||
|
ansi-regex "^5.0.1"
|
||||||
|
|
||||||
|
strip-final-newline@2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad"
|
||||||
|
integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==
|
||||||
|
|
||||||
|
which-module@^2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
|
||||||
|
integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=
|
||||||
|
|
||||||
|
wrap-ansi@^6.2.0:
|
||||||
|
version "6.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53"
|
||||||
|
integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==
|
||||||
|
dependencies:
|
||||||
|
ansi-styles "^4.0.0"
|
||||||
|
string-width "^4.1.0"
|
||||||
|
strip-ansi "^6.0.0"
|
||||||
|
|
||||||
|
y18n@^4.0.0:
|
||||||
|
version "4.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf"
|
||||||
|
integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==
|
||||||
|
|
||||||
|
yargs-parser@^18.1.2:
|
||||||
|
version "18.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0"
|
||||||
|
integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==
|
||||||
|
dependencies:
|
||||||
|
camelcase "^5.0.0"
|
||||||
|
decamelize "^1.2.0"
|
||||||
|
|
||||||
|
yargs@15.4.1:
|
||||||
|
version "15.4.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8"
|
||||||
|
integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==
|
||||||
|
dependencies:
|
||||||
|
cliui "^6.0.0"
|
||||||
|
decamelize "^1.2.0"
|
||||||
|
find-up "^4.1.0"
|
||||||
|
get-caller-file "^2.0.1"
|
||||||
|
require-directory "^2.1.1"
|
||||||
|
require-main-filename "^2.0.0"
|
||||||
|
set-blocking "^2.0.0"
|
||||||
|
string-width "^4.2.0"
|
||||||
|
which-module "^2.0.0"
|
||||||
|
y18n "^4.0.0"
|
||||||
|
yargs-parser "^18.1.2"
|
||||||
5
packages/plugin-node/test/fixtures/nested-lock-and-build/package.json
vendored
Normal file
5
packages/plugin-node/test/fixtures/nested-lock-and-build/package.json
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"is-svg": "4.3.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
22
packages/plugin-node/test/fixtures/nested-lock-and-build/yarn.lock
vendored
Normal file
22
packages/plugin-node/test/fixtures/nested-lock-and-build/yarn.lock
vendored
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||||
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
|
fast-xml-parser@^3.19.0:
|
||||||
|
version "3.21.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-3.21.1.tgz#152a1d51d445380f7046b304672dd55d15c9e736"
|
||||||
|
integrity sha512-FTFVjYoBOZTJekiUsawGsSYV9QL0A+zDYCRj7y34IO6Jg+2IMYEtQa+bbictpdpV8dHxXywqU7C0gRDEOFtBFg==
|
||||||
|
dependencies:
|
||||||
|
strnum "^1.0.4"
|
||||||
|
|
||||||
|
is-svg@4.3.1:
|
||||||
|
version "4.3.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-4.3.1.tgz#8c63ec8c67c8c7f0a8de0a71c8c7d58eccf4406b"
|
||||||
|
integrity sha512-h2CGs+yPUyvkgTJQS9cJzo9lYK06WgRiXUqBBHtglSzVKAuH4/oWsqk7LGfbSa1hGk9QcZ0SyQtVggvBA8LZXA==
|
||||||
|
dependencies:
|
||||||
|
fast-xml-parser "^3.19.0"
|
||||||
|
|
||||||
|
strnum@^1.0.4:
|
||||||
|
version "1.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/strnum/-/strnum-1.0.4.tgz#e97e36a7d6ba9f93d0d6b496b2ed0678d422832b"
|
||||||
|
integrity sha512-lMzNMfDpaQOLt4B2mEbfzYS0+T7dvCXeojnlGf6f1AygvWDMcWyXYaLbyICfjVu29sErR8fnRagQfBW/N/hGgw==
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"private": false,
|
"private": false,
|
||||||
"name": "vercel-plugin-python",
|
"name": "vercel-plugin-python",
|
||||||
"version": "1.0.0-canary.1",
|
"version": "1.0.0-canary.6",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"files": [
|
"files": [
|
||||||
@@ -17,8 +17,8 @@
|
|||||||
"prepublishOnly": "tsc"
|
"prepublishOnly": "tsc"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vercel/build-utils": "2.12.3-canary.17",
|
"@vercel/build-utils": "2.12.3-canary.20",
|
||||||
"@vercel/python": "2.0.6-canary.5"
|
"@vercel/python": "2.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "*",
|
"@types/node": "*",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"private": false,
|
"private": false,
|
||||||
"name": "vercel-plugin-ruby",
|
"name": "vercel-plugin-ruby",
|
||||||
"version": "1.0.0-canary.1",
|
"version": "1.0.0-canary.4",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"files": [
|
"files": [
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
"prepublishOnly": "tsc"
|
"prepublishOnly": "tsc"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vercel/build-utils": "2.12.3-canary.17",
|
"@vercel/build-utils": "2.12.3-canary.20",
|
||||||
"@vercel/ruby": "1.2.8-canary.4"
|
"@vercel/ruby": "1.2.8-canary.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vercel/python",
|
"name": "@vercel/python",
|
||||||
"version": "2.0.6-canary.5",
|
"version": "2.0.6-canary.7",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/python",
|
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/python",
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
createLambda,
|
createLambda,
|
||||||
shouldServe,
|
shouldServe,
|
||||||
debug,
|
debug,
|
||||||
|
NowBuildError,
|
||||||
} from '@vercel/build-utils';
|
} from '@vercel/build-utils';
|
||||||
import { installRequirement, installRequirementsFile } from './install';
|
import { installRequirement, installRequirementsFile } from './install';
|
||||||
|
|
||||||
@@ -22,6 +23,7 @@ async function pipenvConvert(cmd: string, srcDir: string) {
|
|||||||
const out = await execa.stdout(cmd, [], {
|
const out = await execa.stdout(cmd, [], {
|
||||||
cwd: srcDir,
|
cwd: srcDir,
|
||||||
});
|
});
|
||||||
|
debug('Contents of requirements.txt is: ' + out);
|
||||||
fs.writeFileSync(join(srcDir, 'requirements.txt'), out);
|
fs.writeFileSync(join(srcDir, 'requirements.txt'), out);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log('Failed to run "pipfile2req"');
|
console.log('Failed to run "pipfile2req"');
|
||||||
@@ -57,6 +59,10 @@ export const build = async ({
|
|||||||
meta = {},
|
meta = {},
|
||||||
config,
|
config,
|
||||||
}: BuildOptions) => {
|
}: BuildOptions) => {
|
||||||
|
let pipPath = meta.isDev ? 'pip3' : 'pip3.9';
|
||||||
|
let pythonPath = meta.isDev ? 'python3' : 'python3.9';
|
||||||
|
let pythonRuntime = meta.isDev ? 'python3' : 'python3.9';
|
||||||
|
|
||||||
workPath = await downloadFilesInWorkPath({
|
workPath = await downloadFilesInWorkPath({
|
||||||
workPath,
|
workPath,
|
||||||
files: originalFiles,
|
files: originalFiles,
|
||||||
@@ -85,6 +91,8 @@ export const build = async ({
|
|||||||
console.log('Installing required dependencies...');
|
console.log('Installing required dependencies...');
|
||||||
|
|
||||||
await installRequirement({
|
await installRequirement({
|
||||||
|
pythonPath,
|
||||||
|
pipPath,
|
||||||
dependency: 'werkzeug',
|
dependency: 'werkzeug',
|
||||||
version: '1.0.1',
|
version: '1.0.1',
|
||||||
workPath,
|
workPath,
|
||||||
@@ -103,12 +111,43 @@ export const build = async ({
|
|||||||
if (pipfileLockDir) {
|
if (pipfileLockDir) {
|
||||||
debug('Found "Pipfile.lock"');
|
debug('Found "Pipfile.lock"');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const json = await readFile(join(pipfileLockDir, 'Pipfile.lock'), 'utf8');
|
||||||
|
const obj = JSON.parse(json);
|
||||||
|
const version = obj?._meta?.requires?.python_version;
|
||||||
|
if (!meta.isDev) {
|
||||||
|
if (version === '3.6') {
|
||||||
|
pipPath = 'pip3.6';
|
||||||
|
pythonPath = 'python3.6';
|
||||||
|
pythonRuntime = 'python3.6';
|
||||||
|
console.warn(
|
||||||
|
`Warning: Python version "${version}" detected in Pipfile.lock will reach End-Of-Life December 2021. Please upgrade. http://vercel.link/python-version`
|
||||||
|
);
|
||||||
|
} else if (version === '3.9') {
|
||||||
|
pipPath = 'pip3.9';
|
||||||
|
pythonPath = 'python3.9';
|
||||||
|
pythonRuntime = 'python3.9';
|
||||||
|
} else {
|
||||||
|
console.warn(
|
||||||
|
`Warning: Invalid Python version "${version}" detected in Pipfile.lock will be ignored. http://vercel.link/python-version`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
throw new NowBuildError({
|
||||||
|
code: 'INVALID_PIPFILE_LOCK',
|
||||||
|
message: 'Unable to parse Pipfile.lock',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Convert Pipenv.Lock to requirements.txt.
|
// Convert Pipenv.Lock to requirements.txt.
|
||||||
// We use a different`workPath` here because we want `pipfile-requirements` and it's dependencies
|
// We use a different`workPath` here because we want `pipfile-requirements` and it's dependencies
|
||||||
// to not be part of the lambda environment. By using pip's `--target` directive we can isolate
|
// to not be part of the lambda environment. By using pip's `--target` directive we can isolate
|
||||||
// it into a separate folder.
|
// it into a separate folder.
|
||||||
const tempDir = await getWriteableDirectory();
|
const tempDir = await getWriteableDirectory();
|
||||||
await installRequirement({
|
await installRequirement({
|
||||||
|
pythonPath,
|
||||||
|
pipPath,
|
||||||
dependency: 'pipfile-requirements',
|
dependency: 'pipfile-requirements',
|
||||||
version: '0.3.0',
|
version: '0.3.0',
|
||||||
workPath: tempDir,
|
workPath: tempDir,
|
||||||
@@ -130,6 +169,8 @@ export const build = async ({
|
|||||||
debug('Found local "requirements.txt"');
|
debug('Found local "requirements.txt"');
|
||||||
const requirementsTxtPath = fsFiles[requirementsTxt].fsPath;
|
const requirementsTxtPath = fsFiles[requirementsTxt].fsPath;
|
||||||
await installRequirementsFile({
|
await installRequirementsFile({
|
||||||
|
pythonPath,
|
||||||
|
pipPath,
|
||||||
filePath: requirementsTxtPath,
|
filePath: requirementsTxtPath,
|
||||||
workPath,
|
workPath,
|
||||||
meta,
|
meta,
|
||||||
@@ -138,6 +179,8 @@ export const build = async ({
|
|||||||
debug('Found global "requirements.txt"');
|
debug('Found global "requirements.txt"');
|
||||||
const requirementsTxtPath = fsFiles['requirements.txt'].fsPath;
|
const requirementsTxtPath = fsFiles['requirements.txt'].fsPath;
|
||||||
await installRequirementsFile({
|
await installRequirementsFile({
|
||||||
|
pythonPath,
|
||||||
|
pipPath,
|
||||||
filePath: requirementsTxtPath,
|
filePath: requirementsTxtPath,
|
||||||
workPath,
|
workPath,
|
||||||
meta,
|
meta,
|
||||||
@@ -163,7 +206,7 @@ export const build = async ({
|
|||||||
await writeFile(join(workPath, `${handlerPyFilename}.py`), handlerPyContents);
|
await writeFile(join(workPath, `${handlerPyFilename}.py`), handlerPyContents);
|
||||||
|
|
||||||
// Use the system-installed version of `python3` when running via `vercel dev`
|
// Use the system-installed version of `python3` when running via `vercel dev`
|
||||||
const runtime = meta.isDev ? 'python3' : 'python3.6';
|
const runtime = meta.isDev ? 'python3' : pythonRuntime;
|
||||||
|
|
||||||
const globOptions: GlobOptions = {
|
const globOptions: GlobOptions = {
|
||||||
cwd: workPath,
|
cwd: workPath,
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import execa from 'execa';
|
import execa from 'execa';
|
||||||
import { Meta, debug } from '@vercel/build-utils';
|
import { Meta, debug } from '@vercel/build-utils';
|
||||||
const pipPath = 'pip3';
|
|
||||||
|
|
||||||
const makeDependencyCheckCode = (dependency: string) => `
|
const makeDependencyCheckCode = (dependency: string) => `
|
||||||
from importlib import util
|
from importlib import util
|
||||||
@@ -9,10 +8,14 @@ spec = util.find_spec(dep)
|
|||||||
print(spec.origin)
|
print(spec.origin)
|
||||||
`;
|
`;
|
||||||
|
|
||||||
async function isInstalled(dependency: string, cwd: string) {
|
async function isInstalled(
|
||||||
|
pythonPath: string,
|
||||||
|
dependency: string,
|
||||||
|
cwd: string
|
||||||
|
) {
|
||||||
try {
|
try {
|
||||||
const { stdout } = await execa(
|
const { stdout } = await execa(
|
||||||
'python3',
|
pythonPath,
|
||||||
['-c', makeDependencyCheckCode(dependency)],
|
['-c', makeDependencyCheckCode(dependency)],
|
||||||
{
|
{
|
||||||
stdio: 'pipe',
|
stdio: 'pipe',
|
||||||
@@ -33,10 +36,14 @@ dependencies = distutils.text_file.TextFile(filename='${requirementsPath}').read
|
|||||||
pkg_resources.require(dependencies)
|
pkg_resources.require(dependencies)
|
||||||
`;
|
`;
|
||||||
|
|
||||||
async function areRequirementsInstalled(requirementsPath: string, cwd: string) {
|
async function areRequirementsInstalled(
|
||||||
|
pythonPath: string,
|
||||||
|
requirementsPath: string,
|
||||||
|
cwd: string
|
||||||
|
) {
|
||||||
try {
|
try {
|
||||||
await execa(
|
await execa(
|
||||||
'python3',
|
pythonPath,
|
||||||
['-c', makeRequirementsCheckCode(requirementsPath)],
|
['-c', makeRequirementsCheckCode(requirementsPath)],
|
||||||
{
|
{
|
||||||
stdio: 'pipe',
|
stdio: 'pipe',
|
||||||
@@ -49,7 +56,7 @@ async function areRequirementsInstalled(requirementsPath: string, cwd: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function pipInstall(workPath: string, args: string[]) {
|
async function pipInstall(pipPath: string, workPath: string, args: string[]) {
|
||||||
const target = '.';
|
const target = '.';
|
||||||
// See: https://github.com/pypa/pip/issues/4222#issuecomment-417646535
|
// See: https://github.com/pypa/pip/issues/4222#issuecomment-417646535
|
||||||
//
|
//
|
||||||
@@ -66,19 +73,21 @@ async function pipInstall(workPath: string, args: string[]) {
|
|||||||
target,
|
target,
|
||||||
...args,
|
...args,
|
||||||
];
|
];
|
||||||
debug(`Running "pip3 ${cmdArgs.join(' ')}"...`);
|
const pretty = `${pipPath} ${cmdArgs.join(' ')}`;
|
||||||
|
debug(`Running "${pretty}"...`);
|
||||||
try {
|
try {
|
||||||
await execa(pipPath, cmdArgs, {
|
await execa(pipPath, cmdArgs, {
|
||||||
cwd: workPath,
|
cwd: workPath,
|
||||||
stdio: 'pipe',
|
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(`Failed to run "pip3 ${cmdArgs.join(' ')}"`);
|
console.log(`Failed to run "${pretty}"`);
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface InstallRequirementArg {
|
interface InstallRequirementArg {
|
||||||
|
pythonPath: string;
|
||||||
|
pipPath: string;
|
||||||
dependency: string;
|
dependency: string;
|
||||||
version: string;
|
version: string;
|
||||||
workPath: string;
|
workPath: string;
|
||||||
@@ -91,23 +100,27 @@ interface InstallRequirementArg {
|
|||||||
// so vc_init should do runtime version checks to be compatible with any recent
|
// so vc_init should do runtime version checks to be compatible with any recent
|
||||||
// version of its dependencies
|
// version of its dependencies
|
||||||
export async function installRequirement({
|
export async function installRequirement({
|
||||||
|
pythonPath,
|
||||||
|
pipPath,
|
||||||
dependency,
|
dependency,
|
||||||
version,
|
version,
|
||||||
workPath,
|
workPath,
|
||||||
meta,
|
meta,
|
||||||
args = [],
|
args = [],
|
||||||
}: InstallRequirementArg) {
|
}: InstallRequirementArg) {
|
||||||
if (meta.isDev && (await isInstalled(dependency, workPath))) {
|
if (meta.isDev && (await isInstalled(pythonPath, dependency, workPath))) {
|
||||||
debug(
|
debug(
|
||||||
`Skipping ${dependency} dependency installation, already installed in ${workPath}`
|
`Skipping ${dependency} dependency installation, already installed in ${workPath}`
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const exact = `${dependency}==${version}`;
|
const exact = `${dependency}==${version}`;
|
||||||
await pipInstall(workPath, [exact, ...args]);
|
await pipInstall(pipPath, workPath, [exact, ...args]);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface InstallRequirementsFileArg {
|
interface InstallRequirementsFileArg {
|
||||||
|
pythonPath: string;
|
||||||
|
pipPath: string;
|
||||||
filePath: string;
|
filePath: string;
|
||||||
workPath: string;
|
workPath: string;
|
||||||
meta: Meta;
|
meta: Meta;
|
||||||
@@ -115,14 +128,19 @@ interface InstallRequirementsFileArg {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function installRequirementsFile({
|
export async function installRequirementsFile({
|
||||||
|
pythonPath,
|
||||||
|
pipPath,
|
||||||
filePath,
|
filePath,
|
||||||
workPath,
|
workPath,
|
||||||
meta,
|
meta,
|
||||||
args = [],
|
args = [],
|
||||||
}: InstallRequirementsFileArg) {
|
}: InstallRequirementsFileArg) {
|
||||||
if (meta.isDev && (await areRequirementsInstalled(filePath, workPath))) {
|
if (
|
||||||
|
meta.isDev &&
|
||||||
|
(await areRequirementsInstalled(pythonPath, filePath, workPath))
|
||||||
|
) {
|
||||||
debug(`Skipping requirements file installation, already installed`);
|
debug(`Skipping requirements file installation, already installed`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await pipInstall(workPath, ['--upgrade', '-r', filePath, ...args]);
|
await pipInstall(pipPath, workPath, ['--upgrade', '-r', filePath, ...args]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
"version": 2,
|
|
||||||
"builds": [{ "src": "index.py", "use": "@vercel/python" }],
|
|
||||||
"probes": [{ "path": "/", "mustContain": "wsgi:RANDOMNESS_PLACEHOLDER" }]
|
|
||||||
}
|
|
||||||
11
packages/python/test/fixtures/02-wsgi-pipenv/vercel.json
vendored
Normal file
11
packages/python/test/fixtures/02-wsgi-pipenv/vercel.json
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"version": 2,
|
||||||
|
"builds": [{ "src": "index.py", "use": "@vercel/python" }],
|
||||||
|
"probes": [
|
||||||
|
{
|
||||||
|
"path": "/",
|
||||||
|
"mustContain": "wsgi:RANDOMNESS_PLACEHOLDER",
|
||||||
|
"logMustContain": "Warning: Python version \"3.6\" detected in Pipfile.lock will reach End-Of-Life December 2021. Please upgrade."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"version": 2,
|
|
||||||
"builds": [{ "src": "index.py", "use": "@vercel/python" }],
|
|
||||||
"env": {
|
|
||||||
"RANDOMNESS": "RANDOMNESS_PLACEHOLDER"
|
|
||||||
},
|
|
||||||
"probes": [{ "path": "/", "mustContain": "RANDOMNESS_PLACEHOLDER:env" }]
|
|
||||||
}
|
|
||||||
14
packages/python/test/fixtures/03-env-vars/vercel.json
vendored
Normal file
14
packages/python/test/fixtures/03-env-vars/vercel.json
vendored
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"version": 2,
|
||||||
|
"builds": [{ "src": "index.py", "use": "@vercel/python" }],
|
||||||
|
"env": {
|
||||||
|
"RANDOMNESS": "RANDOMNESS_PLACEHOLDER"
|
||||||
|
},
|
||||||
|
"probes": [
|
||||||
|
{
|
||||||
|
"path": "/",
|
||||||
|
"mustContain": "RANDOMNESS_PLACEHOLDER:env",
|
||||||
|
"logMustContain": "Warning: Python version \"3.6\" detected in Pipfile.lock will reach End-Of-Life December 2021. Please upgrade."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
"version": 2,
|
|
||||||
"builds": [{ "src": "**/**.py", "use": "@vercel/python" }],
|
|
||||||
"probes": [{ "path": "/", "mustContain": "pip:RANDOMNESS_PLACEHOLDER" }]
|
|
||||||
}
|
|
||||||
11
packages/python/test/fixtures/04-cowsay-pipfile/vercel.json
vendored
Normal file
11
packages/python/test/fixtures/04-cowsay-pipfile/vercel.json
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"version": 2,
|
||||||
|
"builds": [{ "src": "**/**.py", "use": "@vercel/python" }],
|
||||||
|
"probes": [
|
||||||
|
{
|
||||||
|
"path": "/",
|
||||||
|
"mustContain": "pip:RANDOMNESS_PLACEHOLDER",
|
||||||
|
"logMustContain": "Warning: Invalid Python version \"3.7\" detected in Pipfile.lock will be ignored."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -13,6 +13,6 @@
|
|||||||
"outDir": "dist",
|
"outDir": "dist",
|
||||||
"types": ["node"],
|
"types": ["node"],
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"target": "esnext"
|
"target": "es2018"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,12 @@ async function nowDeploy(bodies, randomness, uploadNowJson) {
|
|||||||
(path.extname(n) === '.sh' ? 0o100755 : 0o100644),
|
(path.extname(n) === '.sh' ? 0o100755 : 0o100644),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const { FORCE_BUILD_IN_REGION, NOW_DEBUG, VERCEL_DEBUG } = process.env;
|
const {
|
||||||
|
FORCE_BUILD_IN_REGION,
|
||||||
|
NOW_DEBUG,
|
||||||
|
VERCEL_DEBUG,
|
||||||
|
VERCEL_BUILDER_DEBUG,
|
||||||
|
} = process.env;
|
||||||
const nowJson = JSON.parse(bodies['vercel.json'] || bodies['now.json']);
|
const nowJson = JSON.parse(bodies['vercel.json'] || bodies['now.json']);
|
||||||
|
|
||||||
const nowDeployPayload = {
|
const nowDeployPayload = {
|
||||||
@@ -34,6 +39,7 @@ async function nowDeploy(bodies, randomness, uploadNowJson) {
|
|||||||
FORCE_BUILD_IN_REGION,
|
FORCE_BUILD_IN_REGION,
|
||||||
NOW_DEBUG,
|
NOW_DEBUG,
|
||||||
VERCEL_DEBUG,
|
VERCEL_DEBUG,
|
||||||
|
VERCEL_BUILDER_DEBUG,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
name: 'test2020',
|
name: 'test2020',
|
||||||
|
|||||||
35
yarn.lock
35
yarn.lock
@@ -2537,7 +2537,7 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@types/yargs-parser" "*"
|
"@types/yargs-parser" "*"
|
||||||
|
|
||||||
"@types/yazl@^2.4.1":
|
"@types/yazl@2.4.2", "@types/yazl@^2.4.1":
|
||||||
version "2.4.2"
|
version "2.4.2"
|
||||||
resolved "https://registry.yarnpkg.com/@types/yazl/-/yazl-2.4.2.tgz#d5f8a4752261badbf1a36e8b49e042dc18ec84bc"
|
resolved "https://registry.yarnpkg.com/@types/yazl/-/yazl-2.4.2.tgz#d5f8a4752261badbf1a36e8b49e042dc18ec84bc"
|
||||||
integrity sha512-T+9JH8O2guEjXNxqmybzQ92mJUh2oCwDDMSSimZSe1P+pceZiFROZLYmcbqkzV5EUwz6VwcKXCO2S2yUpra6XQ==
|
integrity sha512-T+9JH8O2guEjXNxqmybzQ92mJUh2oCwDDMSSimZSe1P+pceZiFROZLYmcbqkzV5EUwz6VwcKXCO2S2yUpra6XQ==
|
||||||
@@ -2613,10 +2613,10 @@
|
|||||||
"@typescript-eslint/types" "4.28.0"
|
"@typescript-eslint/types" "4.28.0"
|
||||||
eslint-visitor-keys "^2.0.0"
|
eslint-visitor-keys "^2.0.0"
|
||||||
|
|
||||||
"@vercel/fun@1.0.1":
|
"@vercel/fun@1.0.3":
|
||||||
version "1.0.1"
|
version "1.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/@vercel/fun/-/fun-1.0.1.tgz#4b25f50c27bbbdca5b7e71f25ef5018701467962"
|
resolved "https://registry.yarnpkg.com/@vercel/fun/-/fun-1.0.3.tgz#5e5c4921a3a3a35ee129ce063e6b3f5c4b1eae48"
|
||||||
integrity sha512-v++KDTdKIr0357sm0AbyYk5jLmsqfUlOKhTsaN0Z/XHpk2YJHOLlGFTDzZPt+8i+ofdPG8lCfht729DHNQskJg==
|
integrity sha512-9LKNosC+Sdx7ZNU7dnj7XXasHzgRkuDnz9+N6JzSVvb0v8KOZN0ky4+wn3c1B0/wF3dTn2W5nEzgmnsrVCDtkQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@tootallnate/once" "2.0.0"
|
"@tootallnate/once" "2.0.0"
|
||||||
async-listen "1.0.0"
|
async-listen "1.0.0"
|
||||||
@@ -2629,6 +2629,7 @@
|
|||||||
node-fetch "2.6.1"
|
node-fetch "2.6.1"
|
||||||
path-match "1.2.4"
|
path-match "1.2.4"
|
||||||
promisepipe "3.0.0"
|
promisepipe "3.0.0"
|
||||||
|
semver "7.3.5"
|
||||||
stat-mode "0.3.0"
|
stat-mode "0.3.0"
|
||||||
stream-to-promise "2.2.0"
|
stream-to-promise "2.2.0"
|
||||||
tar "4.4.18"
|
tar "4.4.18"
|
||||||
@@ -8992,10 +8993,10 @@ node-fetch-npm@^2.0.2:
|
|||||||
json-parse-better-errors "^1.0.0"
|
json-parse-better-errors "^1.0.0"
|
||||||
safe-buffer "^5.1.1"
|
safe-buffer "^5.1.1"
|
||||||
|
|
||||||
node-fetch@2, node-fetch@^2:
|
node-fetch@2:
|
||||||
version "2.6.5"
|
version "2.6.6"
|
||||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.5.tgz#42735537d7f080a7e5f78b6c549b7146be1742fd"
|
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.6.tgz#1751a7c01834e8e1697758732e9efb6eeadfaf89"
|
||||||
integrity sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ==
|
integrity sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==
|
||||||
dependencies:
|
dependencies:
|
||||||
whatwg-url "^5.0.0"
|
whatwg-url "^5.0.0"
|
||||||
|
|
||||||
@@ -9009,6 +9010,13 @@ node-fetch@2.6.1, node-fetch@^2.2.1, node-fetch@^2.3.0, node-fetch@^2.5.0, node-
|
|||||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
|
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
|
||||||
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
|
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
|
||||||
|
|
||||||
|
node-fetch@^2:
|
||||||
|
version "2.6.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.5.tgz#42735537d7f080a7e5f78b6c549b7146be1742fd"
|
||||||
|
integrity sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ==
|
||||||
|
dependencies:
|
||||||
|
whatwg-url "^5.0.0"
|
||||||
|
|
||||||
node-gyp-build@^4.2.2:
|
node-gyp-build@^4.2.2:
|
||||||
version "4.2.3"
|
version "4.2.3"
|
||||||
resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.2.3.tgz#ce6277f853835f718829efb47db20f3e4d9c4739"
|
resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.2.3.tgz#ce6277f853835f718829efb47db20f3e4d9c4739"
|
||||||
@@ -10665,7 +10673,7 @@ semver@6.1.1:
|
|||||||
resolved "https://registry.yarnpkg.com/semver/-/semver-6.1.1.tgz#53f53da9b30b2103cd4f15eab3a18ecbcb210c9b"
|
resolved "https://registry.yarnpkg.com/semver/-/semver-6.1.1.tgz#53f53da9b30b2103cd4f15eab3a18ecbcb210c9b"
|
||||||
integrity sha512-rWYq2e5iYW+fFe/oPPtYJxYgjBm8sC4rmoGdUOgBB7VnwKt6HrL793l2voH1UlsyYZpJ4g0wfjnTEO1s1NP2eQ==
|
integrity sha512-rWYq2e5iYW+fFe/oPPtYJxYgjBm8sC4rmoGdUOgBB7VnwKt6HrL793l2voH1UlsyYZpJ4g0wfjnTEO1s1NP2eQ==
|
||||||
|
|
||||||
semver@7.x, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5:
|
semver@7.3.5, semver@7.x, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5:
|
||||||
version "7.3.5"
|
version "7.3.5"
|
||||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7"
|
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7"
|
||||||
integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==
|
integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==
|
||||||
@@ -12618,6 +12626,13 @@ yazl@2.4.3:
|
|||||||
dependencies:
|
dependencies:
|
||||||
buffer-crc32 "~0.2.3"
|
buffer-crc32 "~0.2.3"
|
||||||
|
|
||||||
|
yazl@2.5.1:
|
||||||
|
version "2.5.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/yazl/-/yazl-2.5.1.tgz#a3d65d3dd659a5b0937850e8609f22fffa2b5c35"
|
||||||
|
integrity sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw==
|
||||||
|
dependencies:
|
||||||
|
buffer-crc32 "~0.2.3"
|
||||||
|
|
||||||
yn@3.1.1, yn@^3.0.0:
|
yn@3.1.1, yn@^3.0.0:
|
||||||
version "3.1.1"
|
version "3.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50"
|
resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50"
|
||||||
|
|||||||
Reference in New Issue
Block a user