Compare commits

..

25 Commits

Author SHA1 Message Date
Steven
3407e6bd1a Publish Canary
- @now/build-utils@2.1.1-canary.0
 - @now/ruby@1.0.3-canary.3
2020-02-29 18:40:49 -05:00
Steven
553ad240f0 [now-build-utils] Fix NODE_ENV assignment (#3862)
Fixes a regression in #3847 which was preventing the user from assigning `NODE_ENV`.

The root cause was that the spread operator (`...`) performs a shallow clone, not a deep clone. So we must perform another spread to clone the user-provided `env` object before deleting `NODE_ENV` during `npm install`.
2020-02-29 18:37:59 -05:00
m5o
964ce1bb5a [now-ruby] update bundler on ruby rack examples (#3856)
* update bundler on ruby rack examples
  * bundle with latest bundler `v2.1.4`
  * update rack to `v2.2.2`
  🔗 https://github.com/zeit/now/pull/3626
2020-02-28 23:45:54 +00:00
Steven
995aa6eddf Publish Canary
- now@17.0.5-canary.7
2020-02-28 17:39:42 -05:00
Steven
095805e3ad [tests] Fix missing await (#3860)
Follow up to #3858
2020-02-28 17:35:02 -05:00
Steven
b461ed238c [tests] Fix code coverage upload (#3859)
The code coverage report stopped uploading after we switched to GitHub Actions because its not a recognized CI provider.

This PR adds a secret with the code coverage token from [codecov.io](https://codecov.io/gh/zeit/now/settings).
2020-02-28 22:13:04 +00:00
Steven
01e7124189 Publish Canary
- now@17.0.5-canary.6
 - @now/ruby@1.0.3-canary.2
2020-02-28 16:21:56 -05:00
Steven
acd3ac2f98 [now-cli] Disable sentry during local development (#3858)
This PR changes the behavior of error reporting and metrics so that developers who build and run Now CLI locally won't accidentally report any metrics. It also prevents errors during CI tests from being reported.

The env vars are only assigned during the publishing step so that the official Now CLI hosted on npm is the only way to report metrics.
2020-02-28 21:20:36 +00:00
Steven
b50e4209e1 [now-cli] Fix "The specified token is not valid" (#3857)
Fixes https://sentry.io/organizations/zeithq/issues/1537743736/?project=1323225&referrer=slack
2020-02-28 19:59:51 +00:00
m5o
147b4e870c [now-ruby] Add test fixture for ActiveSupport gem (#3853)
* add ruby ActiveSupport test fixture
  > ActiveSupport is a Ruby module & a stand-alone component of Rails that includes additional functionality to core Ruby classes, like Array, String & Numeric.
  🔗 https://guides.rubyonrails.org/active_support_core_extensions.html

* add `Date.current + 10.years` calculation example
2020-02-28 12:56:06 -05:00
Steven
df8327d14c [docs] Update readme (#3855)
Updates the readme to mention the Git Integration and fixes some misinformation.
2020-02-28 12:53:40 -05:00
Joe Haddad
fb4d4b5953 Publish Stable
- @now/build-utils@2.1.0
 - @now/next@2.4.0
 - @now/routing-utils@1.7.0
2020-02-28 10:19:47 -05:00
Joe Haddad
de3701c045 Publish Canary
- @now/ruby@1.0.3-canary.1
2020-02-28 10:18:46 -05:00
m5o
9f9b7934cb [now-ruby] Fix ruby test indentation (#3854)
* fix ruby indentation example
  * 💅 indent with 2 spaces for consistency
2020-02-28 14:34:47 +00:00
Steven
bcded1dd17 Publish Canary
- @now/build-utils@2.0.1-canary.3
 - @now/next@2.3.19-canary.2
 - @now/python@1.1.5-canary.1
2020-02-28 08:57:36 -05:00
Steven
8503af75ba [now-python] Fix space encoding 2020-02-28 08:35:27 -05:00
Joe Haddad
158a50f1aa [now-next] Allow Immutable Fallback Artifact (#3850)
* [now-next] Allow Immutable Fallback Artifact

* trigger
2020-02-27 22:00:09 -05:00
Steven
61da552dd6 [now-build-utils] Add test for NODE_ENV behavior (#3852)
This PR adds a test for #3847.
2020-02-27 21:47:27 +00:00
Steven
fa838eecac Publish Canary
- @now/go@1.0.5-canary.0
 - @now/next@2.3.19-canary.1
 - @now/node@1.4.2-canary.0
 - @now/python@1.1.5-canary.0
 - @now/ruby@1.0.3-canary.0
 - @now/static-build@0.14.13-canary.1
2020-02-27 15:52:36 -05:00
Steven
71b6a58783 [all] Exclude @now/build-utils in bundled builder to fix tests (#3848)
This fixes a bug in our `@now/build-utils` tests that pair the current build-utils with a stable builder. Since ncc was bundling `@now/build-utils`, we weren't able to configure a different version and these tests were not actually testing the correct version of build-utils.

A nice side-effect is that each builder will be about 50% smaller (compared by measuring `dist`).
2020-02-27 15:48:03 -05:00
Joe Haddad
22dd78e286 Publish Canary
- @now/build-utils@2.0.1-canary.2
 - @now/next@2.3.19-canary.0
2020-02-27 12:35:52 -05:00
Joe Haddad
e63fcf2630 [now-next] Add Support for Prerender v2 (#3845)
* [now-next] Add Support for Prerender v2

* Copy test suite

* Test that fallback doesn't work for fallback: false

* record omitted lambdas

* Improve test case

* improve omitted routes logic

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2020-02-27 12:33:31 -05:00
Steven
98e1553c2e [now-build-utils] Install all deps regardless of NODE_ENV (#3847)
Some build utilities and SSG Frameworks instruct users to set `NODE_ENV=production` which typically means updating `now.json` to the following:

```json
{
  "build": {
    "env": {
      "NODE_ENV": "production"
    }
  }
}
```

The problem is that this environment variable is assigned during `npm install` or `yarn install` which is the equivalent of running install with the `--production` flag. This flag prevents `devDependencies` from installing. This is almost never what the user intends so they have to remove `now.json` and instead updating their build script to `NODE_ENV=production yarn build`.

This PR improves the experience by deleting `NODE_ENV` during the install step.
2020-02-27 12:14:06 -05:00
Steven
4dc506f17a Publish Canary
- @now/build-utils@2.0.1-canary.1
 - now@17.0.5-canary.5
 - @now/routing-utils@1.6.1-canary.1
2020-02-27 08:05:05 -05:00
Steven
3b396f29e9 [now-build-utils][now-routing-utils] Fix appendRoutesToPhase() when input is null (#3843)
This PR fixes `@now/routing-utils` when the input routes are null. It used to return the empty array but now it will still append.

I also added many more tests to `@now/build-utils` when `featureHandleMiss: true` and refactored the code a bit to make dynamic routes and api routes a little more clear.
2020-02-26 23:30:50 +00:00
69 changed files with 2010 additions and 223 deletions

View File

@@ -94,7 +94,7 @@ jobs:
coverage:
name: Coverage
timeout-minutes: 10
timeout-minutes: 5
needs: [test-unit, test-now-cli, test-now-dev, test-integration]
runs-on: ubuntu-latest
steps:
@@ -107,3 +107,5 @@ jobs:
path: packages/now-cli/.nyc_output
- run: yarn install
- run: yarn workspace now run coverage
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

View File

@@ -20,6 +20,9 @@ jobs:
run: yarn install --check-files --frozen-lockfile
- name: Build
run: yarn build
env:
GA_TRACKING_ID: ${{ secrets.GA_TRACKING_ID }}
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
- name: Publish
run: yarn publish-from-github
env:

1
.gitignore vendored
View File

@@ -12,6 +12,7 @@ coverage
packages/now-cli/.builders
packages/now-cli/assets
packages/now-cli/src/util/dev/templates/*.ts
packages/now-cli/src/util/constants.ts
packages/now-cli/test/**/yarn.lock
!packages/now-cli/test/dev/**/yarn.lock
packages/now-cli/test/**/node_modules

View File

@@ -5,30 +5,18 @@
## Usage
To install the latest version of Now CLI, visit [zeit.co/download](https://zeit.co/download) or run this command:
```
npm i -g now
```
To quickly start a new project, run the following commands:
```
now init # Pick an example project to clone
cd <PROJECT> # Change directory to the newly created project
now # Deploy to the cloud
```
Get started by [Importing a Git Project](https://zeit.co/import) and use `git push` to deploy. Alternatively, you can [install Now CLI](https://zeit.co/download).
## Documentation
For details on how to use Now CLI, check out our [documentation](https://zeit.co/docs/now-cli).
For details on how to use ZEIT Now, check out our [documentation](https://zeit.co/docs).
## Caught a Bug?
1. [Fork](https://help.github.com/articles/fork-a-repo/) this repository to your own GitHub account and then [clone](https://help.github.com/articles/cloning-a-repository/) it to your local device
2. Install dependencies with `yarn install`
3. Compile the code: `yarn build`
4. Link the package to the global module directory: `yarn link`
4. Link the package to the global module directory: `cd ./packages/now-cli && yarn link`
5. You can now start using `now` anywhere inside the command line
As always, you should use `yarn test-unit` to run the tests and see if your changes have broken anything.

View File

@@ -1,6 +1,6 @@
{
"name": "@now/build-utils",
"version": "2.0.1-canary.0",
"version": "2.1.1-canary.0",
"license": "MIT",
"main": "./dist/index.js",
"types": "./dist/index.d.js",

View File

@@ -130,15 +130,15 @@ export async function detectBuilders(
let fallbackEntrypoint: string | null = null;
const preDefaultRoutes: Source[] = [];
const preDynamicRoutes: Source[] = [];
const apiRoutes: Source[] = [];
const dynamicRoutes: Source[] = [];
// API
for (const fileName of sortedFiles) {
const apiBuilder = maybeGetApiBuilder(fileName, apiMatches, options);
if (apiBuilder) {
const { routeError, defaultRoute, dynamicRoute } = getApiRoute(
const { routeError, apiRoute, isDynamic } = getApiRoute(
fileName,
apiSortedFiles,
options,
@@ -156,12 +156,11 @@ export async function detectBuilders(
};
}
if (dynamicRoute) {
preDynamicRoutes.push(dynamicRoute);
}
if (defaultRoute) {
preDefaultRoutes.push(defaultRoute);
if (apiRoute) {
apiRoutes.push(apiRoute);
if (isDynamic) {
dynamicRoutes.push(apiRoute);
}
}
addToUsedFunctions(apiBuilder);
@@ -287,9 +286,9 @@ export async function detectBuilders(
}
}
const routesResult = mergeRoutes(
preDefaultRoutes,
preDynamicRoutes,
const routesResult = getRouteResult(
apiRoutes,
dynamicRoutes,
usedOutputDirectory,
apiBuilders,
frontendBuilder,
@@ -622,16 +621,16 @@ function getApiRoute(
options: Options,
absolutePathCache: Map<string, string>
): {
defaultRoute: Source | null;
dynamicRoute: Source | null;
apiRoute: Source | null;
isDynamic: boolean;
routeError: ErrorResponse | null;
} {
const conflictingSegment = getConflictingSegment(fileName);
if (conflictingSegment) {
return {
defaultRoute: null,
dynamicRoute: null,
apiRoute: null,
isDynamic: false,
routeError: {
code: 'conflicting_path_segment',
message:
@@ -650,8 +649,8 @@ function getApiRoute(
);
return {
defaultRoute: null,
dynamicRoute: null,
apiRoute: null,
isDynamic: false,
routeError: {
code: 'conflicting_file_path',
message:
@@ -669,8 +668,8 @@ function getApiRoute(
);
return {
defaultRoute: out.route,
dynamicRoute: out.isDynamic ? out.route : null,
apiRoute: out.route,
isDynamic: out.isDynamic,
routeError: null,
};
}
@@ -881,9 +880,9 @@ function createRouteFromPath(
return { route, isDynamic };
}
function mergeRoutes(
preDefaultRoutes: Source[],
preDynamicRoutes: Source[],
function getRouteResult(
apiRoutes: Source[],
dynamicRoutes: Source[],
outputDirectory: string,
apiBuilders: Builder[],
frontendBuilder: Builder | null,
@@ -897,7 +896,7 @@ function mergeRoutes(
const redirectRoutes: Route[] = [];
const rewriteRoutes: Route[] = [];
if (preDefaultRoutes && preDefaultRoutes.length > 0) {
if (apiRoutes && apiRoutes.length > 0) {
if (options.featHandleMiss) {
const extSet = detectApiExtensions(apiBuilders);
@@ -932,21 +931,16 @@ function mergeRoutes(
}
}
if (preDynamicRoutes) {
rewriteRoutes.push(...preDynamicRoutes);
}
if (preDefaultRoutes.length) {
rewriteRoutes.push({
src: '^/api(/.*)?$',
status: 404,
continue: true,
});
}
rewriteRoutes.push(...dynamicRoutes);
rewriteRoutes.push({
src: '^/api(/.*)?$',
status: 404,
continue: true,
});
} else {
defaultRoutes.push(...preDefaultRoutes);
defaultRoutes.push(...apiRoutes);
if (preDefaultRoutes.length) {
if (apiRoutes.length) {
defaultRoutes.push({
status: 404,
src: '^/api(/.*)?$',

View File

@@ -206,10 +206,10 @@ export async function runNpmInstall(
debug(`Installing to ${destPath}`);
const { hasPackageLockJson } = await scanParentDirs(destPath);
const opts = { cwd: destPath, ...spawnOpts } || {
cwd: destPath,
env: process.env,
};
const opts: SpawnOptions = { cwd: destPath, ...spawnOpts };
const env = opts.env ? { ...opts.env } : { ...process.env };
delete env.NODE_ENV;
opts.env = env;
if (hasPackageLockJson) {
commandArgs = args.filter(a => a !== '--prefer-offline');
@@ -239,10 +239,7 @@ export async function runBundleInstall(
}
assert(path.isAbsolute(destPath));
const opts = { cwd: destPath, ...spawnOpts } || {
cwd: destPath,
env: process.env,
};
const opts = { cwd: destPath, ...spawnOpts };
await spawnAsync(
'bundle',
@@ -270,10 +267,7 @@ export async function runPipInstall(
}
assert(path.isAbsolute(destPath));
const opts = { cwd: destPath, ...spawnOpts } || {
cwd: destPath,
env: process.env,
};
const opts = { cwd: destPath, ...spawnOpts };
await spawnAsync(
'pip3',

View File

@@ -4,7 +4,7 @@ import FileRef from './file-ref';
import { Lambda } from './lambda';
interface PrerenderOptions {
expiration: number;
expiration: number | false;
lambda: Lambda;
fallback: FileBlob | FileFsRef | FileRef | null;
group?: number;
@@ -13,7 +13,7 @@ interface PrerenderOptions {
export class Prerender {
public type: 'Prerender';
public expiration: number;
public expiration: number | false;
public lambda: Lambda;
public fallback: FileBlob | FileFsRef | FileRef | null;
public group?: number;

View File

@@ -0,0 +1,21 @@
function checkPkgOrThrow(pkgname) {
try {
const dep = require(pkgname);
if (!dep) {
throw new Error('Undefined');
}
} catch (e) {
console.error(`Expected package "${pkgname}" to be installed.`);
process.exit(1);
}
}
// We expect both `dependencies` and `devDependencies` to be installed
// even when NODE_ENV=production.
checkPkgOrThrow('tls-check');
checkPkgOrThrow('exeggcute');
// This is to satisfy `@now/static-build` which needs a `dist` directory.
const { exec } = require('exeggcute');
exec('mkdir dist', __dirname);
exec('echo "node-env:RANDOMNESS_PLACEHOLDER" > dist/index.html', __dirname);

View File

@@ -0,0 +1,6 @@
{
"version": 2,
"builds": [{ "src": "package.json", "use": "@now/static-build" }],
"build": { "env": { "NODE_ENV": "production" } },
"probes": [{ "path": "/", "mustContain": "node-env:RANDOMNESS_PLACEHOLDER" }]
}

View File

@@ -0,0 +1,12 @@
{
"private": true,
"scripts": {
"build": "node build.js"
},
"dependencies": {
"tls-check": "1.0.0"
},
"devDependencies": {
"exeggcute": "1.0.0"
}
}

View File

@@ -0,0 +1,8 @@
{
"version": 2,
"builds": [{ "src": "package.json", "use": "@now/static-build" }],
"build": { "env": { "NODE_ENV": "custom-value:RANDOMNESS_PLACEHOLDER" } },
"probes": [
{ "path": "/", "mustContain": "custom-value:RANDOMNESS_PLACEHOLDER" }
]
}

View File

@@ -0,0 +1,6 @@
{
"private": true,
"scripts": {
"build": "mkdir public && echo $NODE_ENV > public/index.html"
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -15,7 +15,6 @@ To quickly start a new project, run the following commands:
```
now init # Pick an example project
cd <PROJECT> # Change directory to the new project
now dev # Run locally during development
now # Deploy to the cloud
```

View File

@@ -1,6 +1,6 @@
{
"name": "now",
"version": "17.0.5-canary.4",
"version": "17.0.5-canary.7",
"preferGlobal": true,
"license": "Apache-2.0",
"description": "The command-line interface for Now",

View File

@@ -4,7 +4,13 @@ import execa from 'execa';
import { join } from 'path';
import pipe from 'promisepipe';
import { createGzip } from 'zlib';
import { createWriteStream, mkdirp, remove, writeJSON } from 'fs-extra';
import {
createWriteStream,
mkdirp,
remove,
writeJSON,
writeFile,
} from 'fs-extra';
import { getDistTag } from '../src/util/get-dist-tag';
import pkg from '../package.json';
@@ -47,10 +53,34 @@ async function createBuildersTarball() {
);
}
async function createConstants() {
console.log('Creating constants.ts');
const filename = join(dirRoot, 'src/util/constants.ts');
const contents = `// This file is auto-generated
export const GA_TRACKING_ID: string | undefined = ${envToString(
'GA_TRACKING_ID'
)};
export const SENTRY_DSN: string | undefined = ${envToString('SENTRY_DSN')};
`;
await writeFile(filename, contents, 'utf8');
}
function envToString(key: string) {
const value = process.env[key];
if (!value) {
console.log(`- Constant ${key} is not assigned`);
}
return JSON.stringify(value);
}
async function main() {
const isDev = process.argv[2] === '--dev';
if (!isDev) {
// Read the secrets from GitHub Actions and generate a file.
// During local development, these secrets will be empty.
await createConstants();
// Create a tarball from all the `@now` scoped builders which will be bundled
// with Now CLI
await createBuildersTarball();

View File

@@ -284,13 +284,22 @@ export default async function main(
return 0;
}
org = await selectOrg(
output,
'Which scope do you want to deploy to?',
client,
ctx.config.currentTeam,
autoConfirm
);
try {
org = await selectOrg(
output,
'Which scope do you want to deploy to?',
client,
ctx.config.currentTeam,
autoConfirm
);
} catch (err) {
if (err.code === 'NOT_AUTHORIZED' || err.code === 'TEAM_DELETED') {
output.error(err.message);
return 1;
}
throw err;
}
// We use `localConfig` here to read the name
// even though the `now.json` file can change

View File

@@ -37,7 +37,7 @@ export default async function add(
try {
({ contextName } = await getScope(client));
} catch (err) {
if (err.code === 'NOT_AUTHORIZED') {
if (err.code === 'NOT_AUTHORIZED' || err.code === 'TEAM_DELETED') {
output.error(err.message);
return 1;
}

View File

@@ -27,7 +27,7 @@ export default async function add(
try {
({ contextName } = await getScope(client));
} catch (err) {
if (err.code === 'NOT_AUTHORIZED') {
if (err.code === 'NOT_AUTHORIZED' || err.code === 'TEAM_DELETED') {
output.error(err.message);
return 1;
}

View File

@@ -42,7 +42,7 @@ export default async function move(
try {
({ contextName, user } = await getScope(client));
} catch (err) {
if (err.code === 'NOT_AUTHORIZED') {
if (err.code === 'NOT_AUTHORIZED' || err.code === 'TEAM_DELETED') {
output.error(err.message);
return 1;
}
@@ -214,4 +214,4 @@ async function findDestinationMatch(
}
return null;
}
}

View File

@@ -40,7 +40,7 @@ export default async function transferIn(
try {
({ contextName } = await getScope(client));
} catch (err) {
if (err.code === 'NOT_AUTHORIZED') {
if (err.code === 'NOT_AUTHORIZED' || err.code === 'TEAM_DELETED') {
output.error(err.message);
return 1;
}

View File

@@ -9,6 +9,7 @@ import exit from '../util/exit';
import Client from '../util/client.ts';
import logo from '../util/output/logo';
import getScope from '../util/get-scope';
import createOutput from '../util/output';
const e = encodeURIComponent;
@@ -63,13 +64,26 @@ const main = async ctx => {
await exit(0);
}
const output = createOutput({ debug });
const {
authConfig: { token },
config: { currentTeam },
} = ctx;
const client = new Client({ apiUrl, token, currentTeam, debug });
const { contextName } = await getScope(client);
let contextName = null;
try {
({ contextName } = await getScope(client));
} catch (err) {
if (err.code === 'NOT_AUTHORIZED' || err.code === 'TEAM_DELETED') {
output.error(err.message);
return 1;
}
throw err;
}
try {
await run({ client, contextName });

View File

@@ -76,7 +76,17 @@ export default async function({
const stopUserSpinner = wait('Fetching user information');
const client = new Client({ apiUrl, token });
const user = await getUser(client);
let user;
try {
user = await getUser(client);
} catch (err) {
if (err.code === 'NOT_AUTHORIZED' || err.code === 'TEAM_DELETED') {
console.error(error(err.message));
return 1;
}
throw err;
}
stopUserSpinner();

View File

@@ -21,7 +21,17 @@ export default async function({ teams, config, apiUrl, token }) {
const stopUserSpinner = wait('Fetching user information');
const client = new Client({ apiUrl, token, currentTeam });
const user = await getUser(client);
let user;
try {
user = await getUser(client);
} catch (err) {
if (err.code === 'NOT_AUTHORIZED' || err.code === 'TEAM_DELETED') {
console.error(error(err.message));
return 1;
}
throw err;
}
stopUserSpinner();

View File

@@ -40,13 +40,23 @@ export default async function({ apiUrl, token, debug, args, config }) {
const stopUserSpinner = wait('Fetching user information');
const client = new Client({ apiUrl, token });
const user = await getUser(client);
let user;
try {
user = await getUser(client);
} catch (err) {
if (err.code === 'NOT_AUTHORIZED' || err.code === 'TEAM_DELETED') {
console.error(error(err.message));
return 1;
}
throw err;
}
stopUserSpinner();
if (accountIsCurrent) {
currentTeam = {
slug: user.username || user.email
slug: user.username || user.email,
};
} else {
currentTeam = list.find(team => team.id === currentTeam);

View File

@@ -1,2 +0,0 @@
export const GA_TRACKING_ID = 'UA-117491914-3';
export const SENTRY_DSN = 'https://26a24e59ba954011919a524b341b6ab5@sentry.io/1323225';

View File

@@ -8,22 +8,26 @@ import * as configFiles from './config/files';
const config: any = configFiles.getConfigFilePath();
export const shouldCollectMetrics = (
config.collectMetrics === undefined
|| config.collectMetrics === true)
&& process.env.NOW_CLI_COLLECT_METRICS !== '0';
export const shouldCollectMetrics =
(config.collectMetrics === undefined || config.collectMetrics === true) &&
process.env.NOW_CLI_COLLECT_METRICS !== '0' &&
GA_TRACKING_ID;
export const metrics = () => {
const token = typeof config.token === 'string' ? config.token : platform() + release();
export const metrics = (): ua.Visitor => {
const token =
typeof config.token === 'string' ? config.token : platform() + release();
const salt = userInfo().username;
const hash = crypto.pbkdf2Sync(token, salt, 1000, 64, 'sha512').toString('hex').substring(0, 24);
const hash = crypto
.pbkdf2Sync(token, salt, 1000, 64, 'sha512')
.toString('hex')
.substring(0, 24);
return ua(GA_TRACKING_ID, {
return ua(GA_TRACKING_ID || '', {
cid: hash,
strictCidFormat: false,
uid: hash,
headers: {
'User-Agent': userAgent
}
'User-Agent': userAgent,
},
});
}
};

View File

@@ -247,12 +247,14 @@ function testFixtureStdio(directory, fn) {
if (stderr.includes(`Requested port ${port} is already in use`)) {
dev.kill('SIGTERM');
throw new Error(`Failed for "${directory}" with port ${port}.`);
throw new Error(
`Failed for "${directory}" with port ${port} with stderr "${stderr}".`
);
}
if (stderr.includes('Command failed') || stderr.includes('Error!')) {
dev.kill('SIGTERM');
throw new Error(`Failed for "${directory}".`);
throw new Error(`Failed for "${directory}" with stderr "${stderr}".`);
}
});
@@ -461,6 +463,8 @@ test(
t.regex(await rand.text(), /random number/gm);
const rand2 = await fetchWithRetry(`http://localhost:${port}/api/rand.js`);
t.regex(await rand2.text(), /random number/gm);
const notfound = await fetch(`http://localhost:${port}/api`);
t.is(notfound.status, 404);
})
);

View File

@@ -1,4 +1,4 @@
ncc build index.ts -o dist
ncc build install.ts -o dist/install
ncc build index.ts -e @now/build-utils -o dist
ncc build install.ts -e @now/build-utils -o dist/install
mv dist/install/index.js dist/install.js
rm -rf dist/install

View File

@@ -1,6 +1,6 @@
{
"name": "@now/go",
"version": "1.0.4",
"version": "1.0.5-canary.0",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://zeit.co/docs/runtimes#official-runtimes/go",

View File

@@ -7,10 +7,10 @@ cp -v "$bridge_defs" src/now__bridge.ts
tsc
ncc build src/dev-server.ts -o dist/dev
ncc build src/dev-server.ts -e @now/build-utils -o dist/dev
mv dist/dev/index.js dist/dev-server.js
rm -rf dist/dev
ncc build src/index.ts -o dist/main
ncc build src/index.ts -e @now/build-utils -o dist/main
mv dist/main/index.js dist/index.js
rm -rf dist/main

View File

@@ -1,6 +1,6 @@
{
"name": "@now/next",
"version": "2.3.18",
"version": "2.4.0",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://zeit.co/docs/runtimes#official-runtimes/next-js",

View File

@@ -366,11 +366,16 @@ export const build = async ({
// Load the /_next/data routes for both dynamic SSG and SSP pages.
// These must be combined and sorted to prevent conflicts
for (const dataRoute of routesManifest.dataRoutes) {
const ssgDataRoute = prerenderManifest.lazyRoutes[dataRoute.page];
const ssgDataRoute =
prerenderManifest.fallbackRoutes[dataRoute.page] ||
prerenderManifest.legacyBlockingRoutes[dataRoute.page];
// we don't need to add routes for non-lazy SSG routes since
// they have outputs which would override the routes anyways
if (prerenderManifest.routes[dataRoute.page]) {
if (
prerenderManifest.staticRoutes[dataRoute.page] ||
prerenderManifest.omittedRoutes.includes(dataRoute.page)
) {
continue;
}
@@ -669,9 +674,8 @@ export const build = async ({
// Lazily prerendered routes have a fallback `.html` file on newer
// Next.js versions so we need to also not treat it as a static page here.
if (
prerenderManifest.routes[routeName] ||
(prerenderManifest.lazyRoutes[routeName] &&
prerenderManifest.lazyRoutes[routeName].fallback)
prerenderManifest.staticRoutes[routeName] ||
prerenderManifest.fallbackRoutes[routeName]
) {
return;
}
@@ -913,33 +917,49 @@ export const build = async ({
}
let prerenderGroup = 1;
const onPrerenderRoute = (routeKey: string, isLazy: boolean) => {
const onPrerenderRoute = (
routeKey: string,
{ isBlocking, isFallback }: { isBlocking: boolean; isFallback: boolean }
) => {
if (isBlocking && isFallback) {
throw new Error(
'invariant: isBlocking and isFallback cannot both be true'
);
}
// Get the route file as it'd be mounted in the builder output
const routeFileNoExt = routeKey === '/' ? '/index' : routeKey;
const lazyHtmlFallback =
isLazy && prerenderManifest.lazyRoutes[routeKey].fallback;
const htmlFsRef =
isLazy && !lazyHtmlFallback
const htmlFsRef = isBlocking
? // Blocking pages do not have an HTML fallback
null
: new FileFsRef({
fsPath: path.join(
pagesDir,
isFallback
? // Fallback pages have a special file.
prerenderManifest.fallbackRoutes[routeKey].fallback
: // Otherwise, the route itself should exist as a static HTML
// file.
`${routeFileNoExt}.html`
),
});
const jsonFsRef =
// JSON data does not exist for fallback or blocking pages
isFallback || isBlocking
? null
: new FileFsRef({
fsPath: path.join(
pagesDir,
`${lazyHtmlFallback || routeFileNoExt + '.html'}`
),
fsPath: path.join(pagesDir, `${routeFileNoExt}.json`),
});
const jsonFsRef = isLazy
? null
: new FileFsRef({
fsPath: path.join(pagesDir, `${routeFileNoExt}.json`),
});
let initialRevalidate: false | number;
let srcRoute: string | null;
let dataRoute: string;
if (isLazy) {
const pr = prerenderManifest.lazyRoutes[routeKey];
if (isFallback || isBlocking) {
const pr = isFallback
? prerenderManifest.fallbackRoutes[routeKey]
: prerenderManifest.legacyBlockingRoutes[routeKey];
initialRevalidate = 1; // TODO: should Next.js provide this default?
// @ts-ignore
if (initialRevalidate === false) {
@@ -949,7 +969,7 @@ export const build = async ({
srcRoute = null;
dataRoute = pr.dataRoute;
} else {
const pr = prerenderManifest.routes[routeKey];
const pr = prerenderManifest.staticRoutes[routeKey];
({ initialRevalidate, srcRoute, dataRoute } = pr);
}
@@ -975,20 +995,14 @@ export const build = async ({
}
prerenders[outputPathPage] = new Prerender({
expiration:
initialRevalidate === false
? MAX_AGE_ONE_YEAR * 10
: initialRevalidate,
expiration: initialRevalidate,
lambda,
fallback: htmlFsRef,
group: prerenderGroup,
bypassToken: prerenderManifest.bypassToken,
});
prerenders[outputPathData] = new Prerender({
expiration:
initialRevalidate === false
? MAX_AGE_ONE_YEAR * 10
: initialRevalidate,
expiration: initialRevalidate,
lambda,
fallback: jsonFsRef,
group: prerenderGroup,
@@ -998,21 +1012,24 @@ export const build = async ({
++prerenderGroup;
};
Object.keys(prerenderManifest.routes).forEach(route =>
onPrerenderRoute(route, false)
Object.keys(prerenderManifest.staticRoutes).forEach(route =>
onPrerenderRoute(route, { isBlocking: false, isFallback: false })
);
Object.keys(prerenderManifest.lazyRoutes).forEach(route =>
onPrerenderRoute(route, true)
Object.keys(prerenderManifest.fallbackRoutes).forEach(route =>
onPrerenderRoute(route, { isBlocking: false, isFallback: true })
);
Object.keys(prerenderManifest.legacyBlockingRoutes).forEach(route =>
onPrerenderRoute(route, { isBlocking: true, isFallback: false })
);
// We still need to use lazyRoutes if the dataRoutes field
// isn't available for backwards compatibility
if (!(routesManifest && routesManifest.dataRoutes)) {
// Dynamic pages for lazy routes should be handled by the lambda flow.
Object.keys(prerenderManifest.lazyRoutes).forEach(lazyRoute => {
const { dataRouteRegex, dataRoute } = prerenderManifest.lazyRoutes[
lazyRoute
];
[
...Object.entries(prerenderManifest.fallbackRoutes),
...Object.entries(prerenderManifest.legacyBlockingRoutes),
].forEach(([, { dataRouteRegex, dataRoute }]) => {
dataRoutes.push({
// Next.js provided data route regex
src: dataRouteRegex.replace(
@@ -1068,7 +1085,8 @@ export const build = async ({
entryDirectory,
dynamicPages,
false,
routesManifest
routesManifest,
new Set(prerenderManifest.omittedRoutes)
).then(arr =>
arr.map(route => {
route.src = route.src.replace('^', `^${dynamicPrefix}`);
@@ -1076,6 +1094,23 @@ export const build = async ({
})
);
// We need to delete lambdas from output instead of omitting them from the
// start since we rely on them for powering Preview Mode (read above in
// onPrerenderRoute).
prerenderManifest.omittedRoutes.forEach(routeKey => {
// Get the route file as it'd be mounted in the builder output
const routeFileNoExt = path.posix.join(
entryDirectory,
routeKey === '/' ? '/index' : routeKey
);
if (typeof lambdas[routeFileNoExt] === undefined) {
throw new Error(
`invariant: unknown lambda ${routeKey} (lookup: ${routeFileNoExt}) | please report this immediately`
);
}
delete lambdas[routeFileNoExt];
});
return {
output: {
...publicDirectoryFiles,

View File

@@ -354,7 +354,8 @@ export async function getDynamicRoutes(
entryDirectory: string,
dynamicPages: string[],
isDev?: boolean,
routesManifest?: RoutesManifest
routesManifest?: RoutesManifest,
omittedRoutes?: Set<string>
): Promise<Source[]> {
if (!dynamicPages.length) {
return [];
@@ -364,14 +365,16 @@ export async function getDynamicRoutes(
switch (routesManifest.version) {
case 1:
case 2: {
return routesManifest.dynamicRoutes.map(
({ page, regex }: { page: string; regex: string }) => {
return routesManifest.dynamicRoutes
.filter(({ page }) =>
omittedRoutes ? !omittedRoutes.has(page) : true
)
.map(({ page, regex }: { page: string; regex: string }) => {
return {
src: regex,
dest: !isDev ? path.join('/', entryDirectory, page) : page,
};
}
);
});
}
default: {
// update MIN_ROUTES_MANIFEST_VERSION
@@ -617,7 +620,7 @@ export async function createLambdaFromPseudoLayers({
export type NextPrerenderedRoutes = {
bypassToken: string | null;
routes: {
staticRoutes: {
[route: string]: {
initialRevalidate: number | false;
dataRoute: string;
@@ -625,14 +628,24 @@ export type NextPrerenderedRoutes = {
};
};
lazyRoutes: {
legacyBlockingRoutes: {
[route: string]: {
fallback?: string;
routeRegex: string;
dataRoute: string;
dataRouteRegex: string;
};
};
fallbackRoutes: {
[route: string]: {
fallback: string;
routeRegex: string;
dataRoute: string;
dataRouteRegex: string;
};
};
omittedRoutes: string[];
};
export async function getExportIntent(
@@ -717,30 +730,58 @@ export async function getPrerenderManifest(
.catch(() => false);
if (!hasManifest) {
return { routes: {}, lazyRoutes: {}, bypassToken: null };
return {
staticRoutes: {},
legacyBlockingRoutes: {},
fallbackRoutes: {},
bypassToken: null,
omittedRoutes: [],
};
}
const manifest: {
version: 1;
routes: {
[key: string]: {
initialRevalidateSeconds: number | false;
dataRoute: string;
srcRoute: string | null;
};
};
dynamicRoutes: {
[key: string]: {
fallback?: string;
routeRegex: string;
dataRoute: string;
dataRouteRegex: string;
};
};
preview?: {
previewModeId: string;
};
} = JSON.parse(await fs.readFile(pathPrerenderManifest, 'utf8'));
const manifest:
| {
version: 1;
routes: {
[key: string]: {
initialRevalidateSeconds: number | false;
dataRoute: string;
srcRoute: string | null;
};
};
dynamicRoutes: {
[key: string]: {
fallback?: string;
routeRegex: string;
dataRoute: string;
dataRouteRegex: string;
};
};
preview?: {
previewModeId: string;
};
}
| {
version: 2;
routes: {
[route: string]: {
initialRevalidateSeconds: number | false;
srcRoute: string | null;
dataRoute: string;
};
};
dynamicRoutes: {
[route: string]: {
routeRegex: string;
fallback: string | false;
dataRoute: string;
dataRouteRegex: string;
};
};
preview: {
previewModeId: string;
};
} = JSON.parse(await fs.readFile(pathPrerenderManifest, 'utf8'));
switch (manifest.version) {
case 1: {
@@ -748,10 +789,12 @@ export async function getPrerenderManifest(
const lazyRoutes = Object.keys(manifest.dynamicRoutes);
const ret: NextPrerenderedRoutes = {
routes: {},
lazyRoutes: {},
staticRoutes: {},
legacyBlockingRoutes: {},
fallbackRoutes: {},
bypassToken:
(manifest.preview && manifest.preview.previewModeId) || null,
omittedRoutes: [],
};
routes.forEach(route => {
@@ -760,7 +803,7 @@ export async function getPrerenderManifest(
dataRoute,
srcRoute,
} = manifest.routes[route];
ret.routes[route] = {
ret.staticRoutes[route] = {
initialRevalidate:
initialRevalidateSeconds === false
? false
@@ -778,7 +821,68 @@ export async function getPrerenderManifest(
dataRouteRegex,
} = manifest.dynamicRoutes[lazyRoute];
ret.lazyRoutes[lazyRoute] = {
if (fallback) {
ret.fallbackRoutes[lazyRoute] = {
routeRegex,
fallback,
dataRoute,
dataRouteRegex,
};
} else {
ret.legacyBlockingRoutes[lazyRoute] = {
routeRegex,
dataRoute,
dataRouteRegex,
};
}
});
return ret;
}
case 2: {
const routes = Object.keys(manifest.routes);
const lazyRoutes = Object.keys(manifest.dynamicRoutes);
const ret: NextPrerenderedRoutes = {
staticRoutes: {},
legacyBlockingRoutes: {},
fallbackRoutes: {},
bypassToken: manifest.preview.previewModeId,
omittedRoutes: [],
};
routes.forEach(route => {
const {
initialRevalidateSeconds,
dataRoute,
srcRoute,
} = manifest.routes[route];
ret.staticRoutes[route] = {
initialRevalidate:
initialRevalidateSeconds === false
? false
: Math.max(1, initialRevalidateSeconds),
dataRoute,
srcRoute,
};
});
lazyRoutes.forEach(lazyRoute => {
const {
routeRegex,
fallback,
dataRoute,
dataRouteRegex,
} = manifest.dynamicRoutes[lazyRoute];
if (!fallback) {
// Fallback behavior is disabled, all routes would've been provided
// in the top-level `routes` key (`staticRoutes`).
ret.omittedRoutes.push(lazyRoute);
return;
}
ret.fallbackRoutes[lazyRoute] = {
routeRegex,
fallback,
dataRoute,
@@ -789,7 +893,13 @@ export async function getPrerenderManifest(
return ret;
}
default: {
return { routes: {}, lazyRoutes: {}, bypassToken: null };
return {
staticRoutes: {},
legacyBlockingRoutes: {},
fallbackRoutes: {},
bypassToken: null,
omittedRoutes: [],
};
}
}
}

View File

@@ -0,0 +1,5 @@
module.exports = {
generateBuildId() {
return 'testing-build-id';
},
};

View File

@@ -0,0 +1,218 @@
{
"version": 2,
"builds": [{ "src": "package.json", "use": "@now/next" }],
"probes": [
{
"path": "/lambda",
"status": 200,
"responseHeaders": {
"x-now-cache": "MISS"
}
},
{
"path": "/forever",
"status": 200,
"responseHeaders": {
"x-now-cache": "PRERENDER"
}
},
{ "delay": 2000 },
{
"path": "/forever",
"status": 200,
"responseHeaders": {
"x-now-cache": "HIT"
}
},
{
"path": "/another",
"status": 200,
"responseHeaders": {
"x-now-cache": "PRERENDER"
}
},
{ "delay": 2000 },
{
"path": "/another",
"status": 200,
"responseHeaders": {
"x-now-cache": "HIT"
}
},
{
"path": "/blog/post-1",
"status": 200,
"responseHeaders": {
"x-now-cache": "PRERENDER"
}
},
{ "delay": 2000 },
{
"path": "/blog/post-1",
"status": 200,
"responseHeaders": {
"x-now-cache": "HIT"
}
},
{
"path": "/blog/post-2",
"status": 200,
"responseHeaders": {
"x-now-cache": "PRERENDER"
}
},
{ "delay": 2000 },
{
"path": "/blog/post-2",
"status": 200,
"responseHeaders": {
"x-now-cache": "HIT"
}
},
{
"path": "/blog/post-3",
"status": 200,
"mustContain": "loading..."
},
{ "delay": 2000 },
{
"path": "/blog/post-3",
"status": 200,
"responseHeaders": {
"x-now-cache": "/HIT|STALE/"
}
},
{
"path": "/_next/data/testing-build-id/blog/post-4.json",
"status": 200,
"responseHeaders": {
"x-now-cache": "MISS"
}
},
{ "delay": 2000 },
{
"path": "/_next/data/testing-build-id/blog/post-4.json",
"status": 200,
"responseHeaders": {
"x-now-cache": "/HIT|STALE/"
}
},
{
"path": "/blog/post-3",
"status": 200,
"mustContain": "post-3"
},
{
"path": "/blog/post-1/comment-1",
"status": 200,
"responseHeaders": {
"x-now-cache": "PRERENDER"
}
},
{
"path": "/blog/post-2/comment-2",
"status": 200,
"responseHeaders": {
"x-now-cache": "PRERENDER"
}
},
{
"path": "/blog/post-3/comment-3",
"status": 200,
"mustContain": "loading..."
},
{
"path": "/_next/data/testing-build-id/lambda.json",
"status": 404
},
{
"path": "/_next/data/testing-build-id/forever.json",
"status": 200,
"responseHeaders": {
"x-now-cache": "/PRERENDER|HIT/"
}
},
{ "delay": 2000 },
{
"path": "/blog/post-3/comment-3",
"status": 200,
"mustContain": "comment-3"
},
{
"path": "/_next/data/testing-build-id/forever.json",
"status": 200,
"responseHeaders": {
"x-now-cache": "HIT"
}
},
{
"path": "/_next/data/testing-build-id/another.json",
"status": 200,
"responseHeaders": {
"x-now-cache": "/HIT|STALE/"
}
},
{
"path": "/_next/data/testing-build-id/another2.json",
"status": 200,
"responseHeaders": {
"x-now-cache": "PRERENDER"
}
},
{ "delay": 2000 },
{
"path": "/_next/data/testing-build-id/another2.json",
"status": 200,
"responseHeaders": {
"x-now-cache": "HIT"
}
},
{
"path": "/_next/data/testing-build-id/blog/post-1.json",
"status": 200,
"responseHeaders": {
"x-now-cache": "/HIT|STALE|PRERENDER/"
}
},
{
"path": "/_next/data/testing-build-id/blog/post-1337/comment-1337.json",
"status": 200,
"responseHeaders": {
"x-now-cache": "PRERENDER"
}
},
{
"path": "/nofallback/one",
"status": 200,
"mustContain": "one"
},
{
"path": "/nofallback/two",
"status": 200,
"mustContain": "two"
},
{
"path": "/nofallback/nope",
"status": 404,
"mustContain": "This page could not be found"
},
{
"path": "/_next/data/testing-build-id/nofallback/one.json",
"status": 200,
"responseHeaders": {
"x-now-cache": "/HIT|STALE|PRERENDER/"
}
},
{
"path": "/_next/data/testing-build-id/nofallback/two.json",
"status": 200,
"responseHeaders": {
"x-now-cache": "/HIT|STALE|PRERENDER/"
}
},
{
"path": "/_next/data/testing-build-id/nofallback/nope.json",
"status": 404
}
]
}

View File

@@ -0,0 +1,7 @@
{
"dependencies": {
"next": "9.2.3-canary.14",
"react": "^16.8.6",
"react-dom": "^16.8.6"
}
}

View File

@@ -0,0 +1,21 @@
import React from 'react';
// eslint-disable-next-line camelcase
export async function unstable_getStaticProps() {
return {
props: {
world: 'world',
time: new Date().getTime(),
},
revalidate: 5,
};
}
export default ({ world, time }) => {
return (
<>
<p>hello: {world}</p>
<span>time: {time}</span>
</>
);
};

View File

@@ -0,0 +1,21 @@
import React from 'react';
// eslint-disable-next-line camelcase
export async function unstable_getStaticProps() {
return {
props: {
world: 'world',
time: new Date().getTime(),
},
revalidate: 5,
};
}
export default ({ world, time }) => {
return (
<>
<p>hello: {world}</p>
<span>time: {time}</span>
</>
);
};

View File

@@ -0,0 +1,37 @@
import React from 'react';
// eslint-disable-next-line camelcase
export async function unstable_getStaticPaths() {
return {
paths: [
'/blog/post-1/comment-1',
{ params: { post: 'post-2', comment: 'comment-2' } },
'/blog/post-1337/comment-1337',
],
fallback: true,
};
}
// eslint-disable-next-line camelcase
export async function unstable_getStaticProps({ params }) {
return {
props: {
post: params.post,
comment: params.comment,
time: new Date().getTime(),
},
revalidate: 2,
};
}
export default ({ post, comment, time }) => {
if (!post) return <p>loading...</p>;
return (
<>
<p>Post: {post}</p>
<p>Comment: {comment}</p>
<span>time: {time}</span>
</>
);
};

View File

@@ -0,0 +1,37 @@
import React from 'react';
// eslint-disable-next-line camelcase
export async function unstable_getStaticPaths() {
return {
paths: ['/blog/post-1', { params: { post: 'post-2' } }],
fallback: true,
};
}
// eslint-disable-next-line camelcase
export async function unstable_getStaticProps({ params }) {
if (params.post === 'post-10') {
await new Promise(resolve => {
setTimeout(() => resolve(), 1000);
});
}
return {
props: {
post: params.post,
time: (await import('perf_hooks')).performance.now(),
},
revalidate: 10,
};
}
export default ({ post, time }) => {
if (!post) return <p>loading...</p>;
return (
<>
<p>Post: {post}</p>
<span>time: {time}</span>
</>
);
};

View File

@@ -0,0 +1,21 @@
import React from 'react';
// eslint-disable-next-line camelcase
export async function unstable_getStaticProps() {
return {
props: {
world: 'world',
time: new Date().getTime(),
},
revalidate: false,
};
}
export default ({ world, time }) => {
return (
<>
<p>hello: {world}</p>
<span>time: {time}</span>
</>
);
};

View File

@@ -0,0 +1 @@
export default () => 'Hi';

View File

@@ -0,0 +1,5 @@
const Page = ({ data }) => <p>{data} world</p>;
Page.getInitialProps = () => ({ data: 'hello' });
export default Page;

View File

@@ -0,0 +1,31 @@
import React from 'react';
// eslint-disable-next-line camelcase
export async function unstable_getStaticPaths() {
return {
paths: ['/nofallback/one', { params: { slug: 'two' } }],
fallback: false,
};
}
// eslint-disable-next-line camelcase
export async function unstable_getStaticProps({ params }) {
return {
props: {
slug: params.slug,
time: (await import('perf_hooks')).performance.now(),
},
revalidate: 10,
};
}
export default ({ slug, time }) => {
return (
<>
<p>
Slug ({slug.length}): {slug}
</p>
<span>time: {time}</span>
</>
);
};

View File

@@ -21,23 +21,23 @@ mv dist/types dist/index.d.ts
# bundle helpers.ts with ncc
rm dist/helpers.js
ncc build src/helpers.ts -o dist/helpers
ncc build src/helpers.ts -e @now/build-utils -o dist/helpers
mv dist/helpers/index.js dist/helpers.js
rm -rf dist/helpers
# build source-map-support/register for source maps
ncc build ../../node_modules/source-map-support/register -o dist/source-map-support
ncc build ../../node_modules/source-map-support/register -e @now/build-utils -o dist/source-map-support
mv dist/source-map-support/index.js dist/source-map-support.js
rm -rf dist/source-map-support
# build typescript
ncc build ../../node_modules/typescript/lib/typescript -o dist/typescript
ncc build ../../node_modules/typescript/lib/typescript -e @now/build-utils -o dist/typescript
mv dist/typescript/index.js dist/typescript.js
mkdir -p dist/typescript/lib
mv dist/typescript/typescript/lib/*.js dist/typescript/lib/
mv dist/typescript/typescript/lib/*.d.ts dist/typescript/lib/
rm -r dist/typescript/typescript
ncc build src/index.ts -o dist/main
ncc build src/index.ts -e @now/build-utils -o dist/main
mv dist/main/index.js dist/index.js
rm -rf dist/main

View File

@@ -1,6 +1,6 @@
{
"name": "@now/node",
"version": "1.4.1",
"version": "1.4.2-canary.0",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://zeit.co/docs/runtimes#official-runtimes/node-js",

View File

@@ -12,15 +12,5 @@
}
}
}
],
"probes": [
{
"path": "/lighthouse",
"mustContain": "lighthouse:RANDOMNESS_PLACEHOLDER"
},
{
"path": "/screenshot",
"mustContain": "screenshot:RANDOMNESS_PLACEHOLDER"
}
]
}

View File

@@ -0,0 +1,37 @@
const assert = require('assert').strict;
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
async function tryTest({
testName,
deploymentUrl,
fetch,
randomness,
retries = 4,
}) {
try {
const res = await fetch(`https://${deploymentUrl}/${testName}`);
assert.equal(res.status, 200);
const text = await res.text();
assert.equal(text.trim(), `${testName}:${randomness}`);
console.log(`Finished testing "${testName}" probe.`);
} catch (e) {
if (retries === 0) {
console.error(e);
throw e;
}
console.log(`Failed "${testName}" probe. Retries remaining: ${retries}`);
await sleep(1000);
await tryTest({
testName,
deploymentUrl,
fetch,
randomness,
retries: retries - 1,
});
}
}
module.exports = async ({ deploymentUrl, fetch, randomness }) => {
await tryTest({ testName: 'lighthouse', deploymentUrl, fetch, randomness });
await tryTest({ testName: 'screenshot', deploymentUrl, fetch, randomness });
};

View File

@@ -1 +1 @@
ncc build src/index.ts -o dist
ncc build src/index.ts -e @now/build-utils -o dist

View File

@@ -45,6 +45,7 @@ if 'handler' in __now_variables or 'Handler' in __now_variables:
payload = json.loads(event['body'])
path = unquote(payload['path'])
path = path.replace(' ', '%20')
headers = payload['headers']
method = payload['method']
encoding = payload.get('encoding')

View File

@@ -1,6 +1,6 @@
{
"name": "@now/python",
"version": "1.1.4",
"version": "1.1.5-canary.1",
"main": "./dist/index.js",
"license": "MIT",
"homepage": "https://zeit.co/docs/runtimes#official-runtimes/python",

View File

@@ -1,9 +1,16 @@
{
"version": 2,
"builds": [{ "src": "*.py", "use": "@now/python" }],
"routes": [{ "src": "/another", "dest": "custom.py" }],
"routes": [{ "src": "/(.+)", "dest": "custom.py" }],
"probes": [
{ "path": "/?hello=/", "mustContain": "path=/?hello=/" },
{ "path": "/another?hello=/", "mustContain": "path=/another?hello=/" }
{ "path": "/another?hello=/", "mustContain": "path=/another?hello=/" },
{ "path": "/another?hello=/", "mustContain": "path=/another?hello=/" },
{ "path": "/?say=hello%20world", "mustContain": "/?say=hello%20world" },
{ "path": "/?say%20hello=nice", "mustContain": "/?say%20hello=nice" },
{
"path": "/hello%20world?say=nice",
"mustContain": "/hello%20world?say=nice"
}
]
}

View File

@@ -1,6 +1,6 @@
{
"name": "@now/routing-utils",
"version": "1.6.1-canary.0",
"version": "1.7.0",
"description": "ZEIT Now routing utilities",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",

View File

@@ -6,15 +6,12 @@ export function appendRoutesToPhase({
newRoutes,
phase,
}: AppendRoutesToPhaseProps) {
if (prevRoutes === null) {
return [];
}
const routes = prevRoutes ? [...prevRoutes] : [];
if (newRoutes === null || newRoutes.length === 0) {
return prevRoutes;
return routes;
}
let isInPhase = false;
let insertIndex = -1;
const routes = [...prevRoutes];
routes.forEach((r, i) => {
if (isHandler(r)) {

View File

@@ -1,7 +1,7 @@
const { deepEqual } = require('assert');
const { appendRoutesToPhase } = require('../dist/append');
test('appendRoutesToPhase `routes=null`', () => {
test('appendRoutesToPhase `routes=null` and `newRoutes=[]`', () => {
const routes = null;
const newRoutes = [];
const phase = 'filesystem';
@@ -10,7 +10,16 @@ test('appendRoutesToPhase `routes=null`', () => {
deepEqual(actual, expected);
});
test('appendRoutesToPhase `newRoutes=null`', () => {
test('appendRoutesToPhase `routes=null` and one `newRoutes`', () => {
const routes = null;
const newRoutes = [{ src: '/foo', dest: '/bar' }];
const phase = 'filesystem';
const actual = appendRoutesToPhase({ routes, newRoutes, phase });
const expected = [{ handle: 'filesystem' }, ...newRoutes];
deepEqual(actual, expected);
});
test('appendRoutesToPhase `routes=[]` and `newRoutes=null`', () => {
const routes = [];
const newRoutes = null;
const phase = 'filesystem';

View File

@@ -1 +1 @@
ncc build index.ts -o dist
ncc build index.ts -e @now/build-utils -o dist

View File

@@ -1,7 +1,7 @@
{
"name": "@now/ruby",
"author": "Nathan Cahill <nathan@nathancahill.com>",
"version": "1.0.2",
"version": "1.0.3-canary.3",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://zeit.co/docs/runtimes#official-runtimes/ruby",

View File

@@ -1,7 +1,7 @@
require 'cowsay'
Handler = Proc.new do |req, res|
res.status = 200
res['Content-Type'] = 'text/plain'
res.body = Cowsay.say('gem:RANDOMNESS_PLACEHOLDER', 'cow')
res.status = 200
res['Content-Type'] = 'text/plain'
res.body = Cowsay.say('gem:RANDOMNESS_PLACEHOLDER', 'cow')
end

View File

@@ -1,7 +1,7 @@
GEM
remote: https://rubygems.org/
specs:
rack (2.1.1)
rack (2.2.2)
PLATFORMS
ruby
@@ -10,4 +10,4 @@ DEPENDENCIES
rack (~> 2.0)
BUNDLED WITH
1.17.3
2.1.4

View File

@@ -1,7 +1,7 @@
GEM
remote: https://rubygems.org/
specs:
rack (2.1.1)
rack (2.2.2)
PLATFORMS
ruby
@@ -10,4 +10,4 @@ DEPENDENCIES
rack (~> 2.0)
BUNDLED WITH
1.17.3
2.1.4

View File

@@ -0,0 +1,3 @@
source "https://rubygems.org"
gem "activesupport", "~> 6.0"

View File

@@ -0,0 +1,26 @@
GEM
remote: https://rubygems.org/
specs:
activesupport (6.0.2.1)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 0.7, < 2)
minitest (~> 5.1)
tzinfo (~> 1.1)
zeitwerk (~> 2.2)
concurrent-ruby (1.1.6)
i18n (1.8.2)
concurrent-ruby (~> 1.0)
minitest (5.14.0)
thread_safe (0.3.6)
tzinfo (1.2.6)
thread_safe (~> 0.1)
zeitwerk (2.2.2)
PLATFORMS
ruby
DEPENDENCIES
activesupport (~> 6.0)
BUNDLED WITH
2.1.4

View File

@@ -0,0 +1,14 @@
require 'active_support'
require 'active_support/core_ext'
Handler = Proc.new do |request, response|
response.status = 200
response["Content-Type"] = "text/plain"
response.body = <<-BODY
10y+ future:
#{Date.current + 10.years}
test:
gem:RANDOMNESS_PLACEHOLDER
BODY
end

View File

@@ -0,0 +1,5 @@
{
"version": 2,
"builds": [{ "src": "index.rb", "use": "@now/ruby" }],
"probes": [{ "path": "/", "mustContain": "gem:RANDOMNESS_PLACEHOLDER" }]
}

View File

@@ -1,4 +1,4 @@
#!/bin/bash
set -euo pipefail
ncc build src/index.ts -o dist
ncc build src/index.ts -e @now/build-utils -o dist

View File

@@ -1,6 +1,6 @@
{
"name": "@now/static-build",
"version": "0.14.13-canary.0",
"version": "0.14.13-canary.1",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://zeit.co/docs/runtimes#official-runtimes/static-builds",

View File

@@ -91,7 +91,6 @@ async function testDeployment(
bodies['now.json'] = Buffer.from(JSON.stringify(nowJson));
delete bodies['probe.js'];
const { deploymentId, deploymentUrl } = await nowDeploy(bodies, randomness);
console.log('deploymentUrl', `https://${deploymentUrl}`);
for (const probe of nowJson.probes || []) {
console.log('testing', JSON.stringify(probe));