Compare commits

...

41 Commits

Author SHA1 Message Date
Steven
d8a5da6a3e Publish
- @now/bash@0.2.0
 - @now/build-utils@0.5.0
 - @now/cgi@0.1.0
 - @now/go@0.4.0
 - @now/html-minifier@1.1.0
 - @now/lambda@0.5.0
 - @now/md@0.5.0
 - @now/mdx-deck@0.5.0
 - @now/next@0.2.0
 - @now/node-bridge@1.1.0
 - @now/node-server@0.6.0
 - @now/node@0.6.0
 - @now/optipng@0.5.0
 - @now/php-bridge@0.5.0
 - @now/php@0.5.0
 - @now/python@0.1.0
 - @now/rust@0.2.0
 - @now/static-build@0.5.0
 - @now/wordpress@0.5.0
2019-04-25 09:19:19 -04:00
Leo Lamprecht
48f7b72bb2 Added missing workPath type property (#426) 2019-04-25 15:05:41 +02:00
Nathan Rajlich
8e2d5de446 [now-static-build] Add now-dev package.json script (#418)
* [static-build] Add `now-dev` package.json script

This will cause `now dev` to run the `now-dev` script as defined in the
`package.json` file.

`now dev` sets the `$PORT` env variable for use in the `now-dev` script,
which is expected to launch a file watching server (for example `hugo
dev`) on the specified `$PORT`. `now dev` wait for this port to be bound
before returning the `build()` function with the generated assets.

The `watch` array is just "watch everything in the
`path.dirname(entrypoint)`" so any file changed within that directory
will trigger a "re-sync" of the source files that `now dev` is working
with, which the user spawned static build watcher should pick up on and
return the updated assets.

* Use `routes` instead of globbing the output dir

Proxy passing to the development server is a cleaner approach, since it
removes the race condition about the builder not having knowledge of
when a dev watcher server rebuilds, so it's not possible to know how
long to wait to return outputs upon another call to `build()`.

* Make `watch` be an array

Co-Authored-By: TooTallNate <n@n8.io>

* Fix `promise-timeout` require()

* Inherit from `process.env` for `yarn run`

Otherwise ENOENT happens because there is no `PATH` env var set

* Only validate dist dir in production builds

* Fix `routes` and `watch` output

* Remove dev logging, add better timeout error message

* Populate `routes` upon rebuild

* Pass `meta` to the `download()` function

* Revert version change in `package.json`

* Save devport sooner
2019-04-25 08:41:38 -04:00
Steven
2b3efb06be Revert "[now-node] Add initial Builder v2 API (#424)" (#425)
This reverts commit dc3f112d4f.
2019-04-24 22:16:37 -04:00
Connor Davis
13e57bf68f [now/next] Set env variable directly 2019-04-24 16:57:00 -05:00
Nathan Rajlich
dc3f112d4f [now-node] Add initial Builder v2 API (#424)
This is a less complete version of #400, since it's still a WIP.
2019-04-24 14:46:02 -07:00
Connor Davis
da1c423033 Publish
- @now/next@0.2.0-canary.45
2019-04-24 14:57:51 -05:00
Connor Davis
5e1d58f0e8 Set target env variable (#423) 2019-04-25 04:16:08 +09:00
Leo Lamprecht
8940f7fa33 Publish
- @now/next@0.2.0-canary.44
2019-04-24 17:24:39 +00:00
Leo Lamprecht
0aeecd81d3 Pass meta to download() of @now/next (#422) 2019-04-24 19:23:57 +02:00
Joe Haddad
bd4cb3d2a0 Publish
- @now/next@0.2.0-canary.43
2019-04-24 10:50:02 -04:00
Joe Haddad
b3d14f536d Increase heap size for Next.js builds (#419)
* Increase heap size for Next.js builds

* Save some memory for the system
2019-04-24 23:10:27 +09:00
Leo Lamprecht
445d4d84cb Publish
- @now/build-utils@0.4.41-canary.8
 - @now/next@0.2.0-canary.42
2019-04-24 11:06:31 +00:00
Leo Lamprecht
30d4ec8cbd Make download() dedupe (#416)
* Make `download()` dedupe

* Made it work

* Fixed unused var
2019-04-24 13:05:31 +02:00
Nathan Rajlich
51355c263c [now-build-utils] Allow build options for runPackageJsonScript() (#417)
To allow, for example, setting `env` variable.
2019-04-24 10:23:46 +08:00
Steven
ec6d695f24 Add builder v2 tests to @now/next (#415)
* Add builder v2 tests to @now/next

* Add builder v2 tests

* wip

* Return childProcesses for tests
2019-04-23 18:41:26 -04:00
Leo Lamprecht
da910dc097 Publish
- @now/next@0.2.0-canary.41
2019-04-23 19:33:38 +00:00
Leo Lamprecht
00cb55f953 Make @now/next dev work on macOS (#414)
* Make `@now/next` dev work on macOS

* Fixed resolving

* Correctly use cwd

* Removed useless code
2019-04-23 21:32:56 +02:00
Leo Lamprecht
54ee557187 Publish
- @now/next@0.2.0-canary.40
 - @now/node-server@0.5.4-canary.5
 - @now/node@0.5.4-canary.6
2019-04-23 12:01:05 +00:00
Leo Lamprecht
e9a49f658b Develop Next.js app inside workPath (#410)
* Develop Next.js app inside workPath

* Made file watching work

* Define routes the first time

* Added index handling

* Fixed indexing

* Fixed indexing again

* Removed useless code

* Start server in child process
2019-04-23 13:58:50 +02:00
Steven
f3484bc7c9 [now-node] Bump ncc to 0.18.1 (#411)
* [now-node] Bump ncc to 0.18.1

* Fix unit test
2019-04-22 18:20:53 -04:00
Leo Lamprecht
f76d200fd2 Renamed property that has changed its name (#409)
* Define that initial build is needed

* Removed useless code
2019-04-21 20:30:02 +02:00
Leo Lamprecht
24ba90bfeb Wait for Next.js dev server to run before continuing (#408)
* Wait for server better

* Fixed version
2019-04-21 18:48:21 +02:00
Olli Vanhoja
9f0fdada18 Log HTTP status just after fetch returns in deploymentPost() (#406) 2019-04-20 21:40:38 +03:00
Leo Lamprecht
a987c4b298 Publish
- @now/build-utils@0.4.41-canary.7
 - @now/next@0.2.0-canary.39
 - @now/optipng@0.4.9-canary.1
2019-04-20 00:17:07 +00:00
Leo Lamprecht
a746adda93 Only match absolute paths in Next.js builder (#405) 2019-04-20 02:16:20 +02:00
Mark
6a00f02137 @now/optipng using typescript (#397)
Migration `@now/optipng` to typescript

Co-Authored-By: MAPESO <markdrew53@gmail.com>
2019-04-19 19:08:25 -04:00
Leo Lamprecht
65558f8197 Publish
- @now/build-utils@0.4.41-canary.6
 - @now/next@0.2.0-canary.38
2019-04-19 21:58:05 +00:00
Leo Lamprecht
a8bf77091f Made now-next invoke next dev (#398)
* Made `now-next` invoke `next dev`

* Even cleaner

* Fixed some types

* Fixed dirs

* Start server if initial build

* Added remaining types

* Added missing type

* Made property optional

* Fixed types

* Expose that the Builder is continuous

* Added more details

* Removed useless code

* Made it work

* Removed useless crap

* Added new version

* Synced up

* Refined logging

* Fixed routes

* Correctly parse request paths

* Fixed routes

* Fixed GET parameter issues
2019-04-19 23:54:47 +02:00
Steven
ee179b9b52 Add fetchTokenWithRetry for bad json response (#403)
This is a quick fix for our token problem by attempting to retry the fetch.

> FetchError: invalid json response body at NOW_TOKEN_FACTORY_URL reason: Unexpected token A in JSON at position 0

The root cause is in the token factory because it throws `Confirmation incomplete` but that will be harder to track down and fix.
2019-04-19 12:59:22 -04:00
Nathan Rajlich
b480b07cce [now-build-utils] Add meta object type definition for build() (#399)
* [now-build-utils] Add `meta` object type definition for `build()`

* Add `meta.requestPath`
2019-04-18 19:30:01 -07:00
Steven
fc8452abfd Publish
- @now/next@0.2.0-canary.1
 - @now/node-server@0.5.4-canary.4
 - @now/node@0.5.4-canary.5
2019-04-18 09:12:32 -04:00
Steven
48b6d0ebfc [now-node] Enable source maps so errors show proper stack trace (#391)
This will increase the lambda size a little bit but it will make for a much better user experience when debugging uncaught errors.

I also added a test so we don't regress.
2019-04-17 17:53:49 -04:00
Connor Davis
a3d6cea3c6 Migrate @now/next to Builders v2 + TS (#379) 2019-04-16 17:24:26 -05:00
Steven
8a61b1b513 Publish
- @now/build-utils@0.4.41-canary.5
 - @now/next@0.1.3-canary.15
 - @now/node@0.5.4-canary.4
2019-04-16 10:53:49 -04:00
Sophearak Tha
50e648d28a [@now/node] Make sure includeFiles have proper path inside lambda (#387)
Fixes #381 

Demo(public): https://now-node-fs.sophearak.now.sh/
2019-04-16 09:41:04 -04:00
Amio
52994bfe26 [build-utils] add default shouldServe and type ShouldServeOptions (#382)
Added the `ShouldServeParam` type declaration and default implementation for `shouldServe` for builder maker to import.
2019-04-16 19:51:51 +08:00
Connor Davis
1339f17585 Update index.js 2019-04-16 03:30:33 -05:00
Nathan Rajlich
9dd12cf1a7 Publish
- @now/build-utils@0.4.41-canary.4
2019-04-15 18:18:37 -07:00
Nathan Rajlich
6dab09f38e [now-build-utils] Create zip files with symlinks properly (#388)
After #359, creating zip files with symlinks would be corrupted because
the "target" of the symlink was actually the file contents.

This commit fixes the zip file construction logic to handle symlinks by
placing the target of the symlink as the "contents" of the file in the
zip. The unit test relies on GNU `unzip` and checks that it creates the
symlink from the produced zip file as expected.
2019-04-15 12:32:28 -07:00
Amio
c79d7be591 Mute stdio for runNpmInstall by default (#338)
Output logs if error occurs
2019-04-16 00:13:51 +08:00
100 changed files with 1830 additions and 373 deletions

View File

@@ -4,5 +4,7 @@
/packages/now-go/go/*
/packages/now-build-utils/dist/*
/packages/now-node/dist/*
/packages/now-next/dist/*
/packages/now-node-bridge/*
/packages/now-python/*
/packages/now-optipng/dist/*

View File

@@ -8,7 +8,7 @@
},
"overrides": [
{
"files": ["test/**"],
"files": ["**/test/**"],
"rules": {
"import/no-extraneous-dependencies": 0
},

View File

@@ -1,3 +1,4 @@
{
"singleQuote": true
"singleQuote": true,
"trailingComma": "es5"
}

View File

@@ -1,4 +1,8 @@
module.exports = {
testEnvironment: 'node',
collectCoverageFrom: ['packages/**/*.{js,jsx}', '!**/node_modules/**'],
collectCoverageFrom: [
'packages/(!test)/**/*.{js,jsx}',
'!**/node_modules/**',
'!**/test/**',
],
};

View File

@@ -27,6 +27,10 @@
"prettier --write",
"eslint --fix",
"git add"
],
"*.ts": [
"prettier --write",
"git add"
]
},
"devDependencies": {

View File

@@ -1,6 +1,6 @@
{
"name": "@now/bash",
"version": "0.1.5-canary.1",
"version": "0.2.0",
"description": "Now 2.0 builder for HTTP endpoints written in Bash",
"main": "index.js",
"author": "Nathan Rajlich <nate@zeit.co>",

View File

@@ -1,2 +1,3 @@
dist
test/symlinks-out
test/symlinks.zip

View File

@@ -1,6 +1,6 @@
{
"name": "@now/build-utils",
"version": "0.4.41-canary.3",
"version": "0.5.0",
"license": "MIT",
"main": "./dist/index.js",
"types": "./dist/index.d.js",
@@ -33,6 +33,7 @@
"@types/glob": "^7.1.1",
"@types/node-fetch": "^2.1.6",
"@types/yazl": "^2.4.1",
"execa": "^1.0.0",
"typescript": "3.3.4000"
}
}

View File

@@ -1,7 +1,7 @@
import path from 'path';
import FileFsRef from '../file-fs-ref';
import { File, Files } from '../types';
import { mkdirp, readlink, symlink } from 'fs-extra';
import { File, Files, Meta } from '../types';
import { remove, mkdirp, readlink, symlink } from 'fs-extra';
export interface DownloadedFiles {
[filePath: string]: FileFsRef
@@ -29,13 +29,31 @@ async function downloadFile(file: File, fsPath: string): Promise<FileFsRef> {
}
}
export default async function download(files: Files, basePath: string): Promise<DownloadedFiles> {
async function removeFile(basePath: string, fileMatched: string) {
const file = path.join(basePath, fileMatched);
await remove(file);
}
export default async function download(files: Files, basePath: string, meta?: Meta): Promise<DownloadedFiles> {
const files2: DownloadedFiles = {};
const { filesChanged = null, filesRemoved = null } = meta || {};
await Promise.all(
Object.keys(files).map(async (name) => {
// If the file does not exist anymore, remove it.
if (Array.isArray(filesRemoved) && filesRemoved.includes(name)) {
await removeFile(basePath, name);
return;
}
// If a file didn't change, do not re-download it.
if (Array.isArray(filesChanged) && !filesChanged.includes(name)) {
return;
}
const file = files[name];
const fsPath = path.join(basePath, name);
files2[name] = await downloadFile(file, fsPath);
}),
);

View File

@@ -5,11 +5,27 @@ import { spawn, SpawnOptions } from 'child_process';
function spawnAsync(command: string, args: string[], cwd: string, opts: SpawnOptions = {}) {
return new Promise<void>((resolve, reject) => {
const child = spawn(command, args, { stdio: 'inherit', cwd, ...opts });
const stderrLogs: Buffer[] = []
opts = { stdio: 'inherit', cwd, ...opts };
const child = spawn(command, args, opts);
if (opts.stdio === 'pipe'){
child.stderr.on('data', data => stderrLogs.push(data));
}
child.on('error', reject);
child.on('close', (code, signal) => (code !== 0
? reject(new Error(`Exited with ${code || signal}`))
: resolve()));
child.on('close', (code, signal) => {
if (code === 0) {
return resolve()
}
const errorLogs = stderrLogs.map(line => line.toString()).join('');
if (opts.stdio !== 'inherit') {
reject(new Error(`Exited with ${code || signal}\n${errorLogs}`));
} else {
reject(new Error(`Exited with ${code || signal}`));
}
});
});
}
@@ -75,22 +91,32 @@ export async function installDependencies(destPath: string, args: string[] = [])
// Node.js version that `@now/node` and `@now/node-server` use
npm_config_target: '8.10.0',
},
stdio: 'pipe'
};
if (hasPackageLockJson) {
commandArgs = args.filter(a => a !== '--prefer-offline');
await spawnAsync('npm', ['install'].concat(commandArgs), destPath, opts);
await spawnAsync(
'npm',
['install'].concat(commandArgs),
destPath,
opts as SpawnOptions
);
} else {
await spawnAsync(
'yarn',
['--cwd', destPath].concat(commandArgs),
destPath,
opts,
opts as SpawnOptions,
);
}
}
export async function runPackageJsonScript(destPath: string, scriptName: string) {
export async function runPackageJsonScript(
destPath: string,
scriptName: string,
opts?: SpawnOptions
) {
assert(path.isAbsolute(destPath));
const { hasScript, hasPackageLockJson } = await scanParentDirs(
destPath,
@@ -100,10 +126,10 @@ export async function runPackageJsonScript(destPath: string, scriptName: string)
if (hasPackageLockJson) {
console.log(`running "npm run ${scriptName}"`);
await spawnAsync('npm', ['run', scriptName], destPath);
await spawnAsync('npm', ['run', scriptName], destPath, opts);
} else {
console.log(`running "yarn run ${scriptName}"`);
await spawnAsync('yarn', ['--cwd', destPath, 'run', scriptName], destPath);
await spawnAsync('yarn', ['--cwd', destPath, 'run', scriptName], destPath, opts);
}
return true;

View File

@@ -1,7 +1,7 @@
import FileBlob from './file-blob';
import FileFsRef from './file-fs-ref';
import FileRef from './file-ref';
import { File, Files, AnalyzeOptions, BuildOptions, PrepareCacheOptions } from './types';
import { File, Files, AnalyzeOptions, BuildOptions, PrepareCacheOptions, ShouldServeOptions, Meta } from './types';
import { Lambda, createLambda } from './lambda';
import download from './fs/download';
import getWriteableDirectory from './fs/get-writable-directory'
@@ -9,6 +9,7 @@ import glob from './fs/glob';
import rename from './fs/rename';
import { installDependencies, runPackageJsonScript, runNpmInstall, runShellScript } from './fs/run-user-scripts';
import streamToBuffer from './fs/stream-to-buffer';
import shouldServe from './should-serve';
export {
FileBlob,
@@ -16,6 +17,7 @@ export {
FileRef,
Files,
File,
Meta,
Lambda,
createLambda,
download,
@@ -27,4 +29,6 @@ export {
AnalyzeOptions,
BuildOptions,
PrepareCacheOptions,
ShouldServeOptions,
shouldServe,
};

View File

@@ -1,8 +1,11 @@
import assert from 'assert';
import Sema from 'async-sema';
import { ZipFile } from 'yazl';
import streamToBuffer from './fs/stream-to-buffer';
import { readlink } from 'fs-extra';
import { Files } from './types';
import FileFsRef from './file-fs-ref';
import { isSymbolicLink } from './fs/download';
import streamToBuffer from './fs/stream-to-buffer';
interface Environment {
[key: string]: string;
@@ -52,22 +55,9 @@ export async function createLambda({
assert(typeof environment === 'object', '"environment" is not an object');
await sema.acquire();
try {
const zipFile = new ZipFile();
const zipBuffer = await new Promise<Buffer>((resolve, reject) => {
Object.keys(files)
.sort()
.forEach((name) => {
const file = files[name];
const stream = file.toStream() as import('stream').Readable;
stream.on('error', reject);
zipFile.addReadStream(stream, name, { mode: file.mode, mtime });
});
zipFile.end();
streamToBuffer(zipFile.outputStream).then(resolve).catch(reject);
});
const zipBuffer = await createZip(files);
return new Lambda({
zipBuffer,
handler,
@@ -78,3 +68,37 @@ export async function createLambda({
sema.release();
}
}
export async function createZip(files: Files): Promise<Buffer> {
const names = Object.keys(files).sort();
const symlinkTargets = new Map<string, string>();
for (const name of names) {
const file = files[name];
if (file.mode && isSymbolicLink(file.mode) && file.type === 'FileFsRef') {
const symlinkTarget = await readlink((file as FileFsRef).fsPath);
symlinkTargets.set(name, symlinkTarget);
}
}
const zipFile = new ZipFile();
const zipBuffer = await new Promise<Buffer>((resolve, reject) => {
for (const name of names) {
const file = files[name];
const opts = { mode: file.mode, mtime };
const symlinkTarget = symlinkTargets.get(name);
if (typeof symlinkTarget === 'string') {
zipFile.addBuffer(Buffer.from(symlinkTarget, 'utf8'), name, opts);
} else {
const stream = file.toStream() as import('stream').Readable;
stream.on('error', reject);
zipFile.addReadStream(stream, name, opts);
}
}
zipFile.end();
streamToBuffer(zipFile.outputStream).then(resolve).catch(reject);
});
return zipBuffer;
}

View File

@@ -0,0 +1,27 @@
import { parse } from 'path';
import { ShouldServeOptions } from './types';
import FileFsRef from './file-fs-ref';
export default function shouldServe({
entrypoint,
files,
requestPath
}: ShouldServeOptions): boolean {
requestPath = requestPath.replace(/\/$/, ''); // sanitize trailing '/'
entrypoint = entrypoint.replace(/\\/, '/'); // windows compatibility
if (entrypoint === requestPath && hasProp(files, entrypoint)) {
return true;
}
const { dir, name } = parse(entrypoint);
if (name === 'index' && dir === requestPath && hasProp(files, entrypoint)) {
return true;
}
return false;
}
function hasProp(obj: { [path: string]: FileFsRef }, key: string): boolean {
return Object.hasOwnProperty.call(obj, key)
}

View File

@@ -1,22 +1,35 @@
import FileRef from './file-ref';
import FileFsRef from './file-fs-ref';
export interface File {
type: string;
mode: number;
toStream: () => NodeJS.ReadableStream;
fsPath?: string;
}
export interface Files {
[filePath: string]: File
[filePath: string]: File;
}
export interface Config {
[key: string]: string
[key: string]: string;
}
export interface Meta {
isDev?: boolean;
requestPath?: string;
filesChanged?: string[];
filesRemoved?: string[];
}
export interface AnalyzeOptions {
/**
* All source files of the project
*/
files: Files;
files: {
[filePath: string]: FileRef;
};
/**
* Name of entrypoint file for this particular build job. Value
@@ -39,7 +52,6 @@ export interface AnalyzeOptions {
config: Config;
}
export interface BuildOptions {
/**
* All source files of the project
@@ -65,6 +77,13 @@ export interface BuildOptions {
* in `now.json`.
*/
config: Config;
/**
* Metadata related to the invoker of the builder, used by `now dev`.
* Builders may use the properties on this object to change behavior based
* on the build environment.
*/
meta?: Meta;
}
export interface PrepareCacheOptions {
@@ -99,3 +118,37 @@ export interface PrepareCacheOptions {
*/
config: Config;
}
export interface ShouldServeOptions {
/**
* A path string from a request.
*/
requestPath: string;
/**
* Name of entrypoint file for this particular build job. Value
* `files[entrypoint]` is guaranteed to exist and be a valid File reference.
* `entrypoint` is always a discrete file and never a glob, since globs are
* expanded into separate builds at deployment time.
*/
entrypoint: string;
/**
* All source files of the project
*/
files: {
[path: string]: FileFsRef;
};
/**
* A writable temporary directory where you are encouraged to perform your
* build process. This directory will be populated with the restored cache.
*/
workPath: string;
/**
* An arbitrary object passed by the user in the build definition defined
* in `now.json`.
*/
config: Config;
}

View File

@@ -1,8 +1,11 @@
/* global beforeAll, expect, it, jest */
const path = require('path');
const fs = require('fs-extra');
// eslint-disable-next-line import/no-extraneous-dependencies
const execa = require('execa');
const assert = require('assert');
const { glob, download } = require('../');
const { createZip } = require('../dist/lambda');
const {
packAndDeploy,
@@ -39,6 +42,28 @@ it('should re-create symlinks properly', async () => {
assert(aStat.isFile());
});
it('should create zip files with symlinks properly', async () => {
const files = await glob('**', path.join(__dirname, 'symlinks'));
assert.equal(Object.keys(files).length, 2);
const outFile = path.join(__dirname, 'symlinks.zip');
await fs.remove(outFile);
const outDir = path.join(__dirname, 'symlinks-out');
await fs.remove(outDir);
await fs.mkdirp(outDir);
await fs.writeFile(outFile, await createZip(files));
await execa('unzip', [outFile], { cwd: outDir });
const [linkStat, aStat] = await Promise.all([
fs.lstat(path.join(outDir, 'link.txt')),
fs.lstat(path.join(outDir, 'a.txt')),
]);
assert(linkStat.isSymbolicLink());
assert(aStat.isFile());
});
// own fixtures
const fixturesPath = path.resolve(__dirname, 'fixtures');

View File

@@ -1,6 +1,6 @@
{
"name": "@now/cgi",
"version": "0.0.16-canary.0",
"version": "0.1.0",
"license": "MIT",
"repository": {
"type": "git",

View File

@@ -1,6 +1,6 @@
{
"name": "@now/go",
"version": "0.3.1-canary.3",
"version": "0.4.0",
"license": "MIT",
"repository": {
"type": "git",

View File

@@ -1,6 +1,6 @@
{
"name": "@now/html-minifier",
"version": "1.0.8-canary.1",
"version": "1.1.0",
"license": "MIT",
"repository": {
"type": "git",

View File

@@ -1,6 +1,6 @@
{
"name": "@now/lambda",
"version": "0.4.10-canary.1",
"version": "0.5.0",
"license": "MIT",
"repository": {
"type": "git",

View File

@@ -1,6 +1,6 @@
{
"name": "@now/md",
"version": "0.4.10-canary.2",
"version": "0.5.0",
"license": "MIT",
"repository": {
"type": "git",

View File

@@ -1,6 +1,6 @@
{
"name": "@now/mdx-deck",
"version": "0.4.19-canary.2",
"version": "0.5.0",
"license": "MIT",
"repository": {
"type": "git",

2
packages/now-next/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
/dist
/src/now__bridge.d.ts

View File

@@ -0,0 +1,11 @@
#!/bin/bash
set -euo pipefail
bridge_entrypoint="$(node -p 'require.resolve("@now/node-bridge")')"
bridge_defs="$(dirname "$bridge_entrypoint")/bridge.d.ts"
if [ ! -e "$bridge_defs" ]; then
yarn install --cwd "$bridge_entrypoint"
fi
cp -v "$bridge_defs" src/now__bridge.d.ts

View File

@@ -1,9 +1,12 @@
{
"name": "@now/next",
"version": "0.1.3-canary.14",
"version": "0.2.0",
"license": "MIT",
"main": "./dist/index",
"scripts": {
"build": "tsc"
"build": "./getBridgeTypes.sh && tsc",
"test": "npm run build && jest",
"prepublish": "yarn run build"
},
"repository": {
"type": "git",
@@ -11,10 +14,20 @@
"directory": "packages/now-next"
},
"dependencies": {
"@now/node-bridge": "^1.0.2-canary.2",
"execa": "^1.0.0",
"@now/node-bridge": "^1.1.0",
"fs-extra": "^7.0.0",
"resolve-from": "^4.0.0",
"get-port": "^5.0.0",
"resolve-from": "^5.0.0",
"semver": "^5.6.0"
},
"files": [
"dist"
],
"devDependencies": {
"@types/next-server": "^8.0.0",
"@types/resolve-from": "^5.0.1",
"@types/semver": "^6.0.0",
"jest": "^24.7.1",
"typescript": "^3.4.3"
}
}

View File

@@ -0,0 +1,41 @@
import resolveFrom from 'resolve-from';
import { parse } from 'url';
import getPort from 'get-port';
import { createServer } from 'http';
export interface ProcessEnv {
[key: string]: string;
}
async function main(env: ProcessEnv, cwd: string) {
const next = require(resolveFrom(cwd, 'next'));
const app = next({ dev: true, dir: cwd });
const handler = app.getRequestHandler();
const openPort = await getPort({
port: [ 5000, 4000 ]
});
const url = `http://localhost:${openPort}`;
// Prepare for incoming requests
await app.prepare();
createServer((req, res) => {
const parsedUrl = parse(req.url || '', true);
handler(req, res, parsedUrl);
}).listen(openPort, (error: NodeJS.ErrnoException) => {
if (error) {
console.error(error);
process.exit(1);
return;
}
if (process.send) {
process.send(url);
}
});
}
main(process.env as ProcessEnv, process.cwd());

View File

@@ -1,55 +1,58 @@
const { createLambda } = require('@now/build-utils/lambda'); // eslint-disable-line import/no-extraneous-dependencies
const download = require('@now/build-utils/fs/download'); // eslint-disable-line import/no-extraneous-dependencies
const FileFsRef = require('@now/build-utils/file-fs-ref'); // eslint-disable-line import/no-extraneous-dependencies
const FileBlob = require('@now/build-utils/file-blob'); // eslint-disable-line import/no-extraneous-dependencies
const resolveFrom = require('resolve-from');
const path = require('path');
const url = require('url');
const {
import { ChildProcess, fork, SpawnOptions } from 'child_process';
import {
pathExists,
readFile,
unlink as unlinkFile,
writeFile,
} from 'fs-extra';
import os from 'os';
import path from 'path';
import semver from 'semver';
import {
BuildOptions,
createLambda,
download,
FileBlob,
FileFsRef,
Files,
glob,
Lambda,
PrepareCacheOptions,
runNpmInstall,
runPackageJsonScript,
} = require('@now/build-utils/fs/run-user-scripts'); // eslint-disable-line import/no-extraneous-dependencies
const glob = require('@now/build-utils/fs/glob'); // eslint-disable-line import/no-extraneous-dependencies
const {
readFile,
writeFile,
unlink: unlinkFile,
remove: removePath,
pathExists,
} = require('fs-extra');
const semver = require('semver');
const nextLegacyVersions = require('./legacy-versions');
const {
} from '@now/build-utils';
import nextLegacyVersions from './legacy-versions';
import {
excludeFiles,
validateEntrypoint,
getNextConfig,
getPathsInside,
getRoutes,
includeOnlyEntryDirectory,
normalizePackageJson,
onlyStaticDirectory,
getNextConfig,
} = require('./utils');
stringMap,
validateEntrypoint,
} from './utils';
/** @typedef { import('@now/build-utils/file-ref').Files } Files */
/** @typedef { import('@now/build-utils/fs/download').DownloadedFiles } DownloadedFiles */
interface BuildParamsMeta {
isDev: boolean | undefined;
}
/**
* @typedef {Object} BuildParamsMeta
* @property {boolean} [isDev] - Files object
* @property {?string} [requestPath] - Entrypoint specified for the builder
*/
interface BuildParamsType extends BuildOptions {
files: Files;
entrypoint: string;
workPath: string;
meta: BuildParamsMeta;
}
/**
* @typedef {Object} BuildParamsType
* @property {Files} files - Files object
* @property {string} entrypoint - Entrypoint specified for the builder
* @property {string} workPath - Working directory for this build
* @property {BuildParamsMeta} [meta] - Various meta settings
*/
export const version = 2;
/**
* Read package.json from files
* @param {string} entryPath
*/
async function readPackageJson(entryPath) {
async function readPackageJson(entryPath: string) {
const packagePath = path.join(entryPath, 'package.json');
try {
@@ -62,29 +65,28 @@ async function readPackageJson(entryPath) {
/**
* Write package.json
* @param {string} workPath
* @param {Object} packageJson
*/
async function writePackageJson(workPath, packageJson) {
async function writePackageJson(workPath: string, packageJson: Object) {
await writeFile(
path.join(workPath, 'package.json'),
JSON.stringify(packageJson, null, 2),
JSON.stringify(packageJson, null, 2)
);
}
/**
* Write .npmrc with npm auth token
* @param {string} workPath
* @param {string} token
*/
async function writeNpmRc(workPath, token) {
async function writeNpmRc(workPath: string, token: string) {
await writeFile(
path.join(workPath, '.npmrc'),
`//registry.npmjs.org/:_authToken=${token}`,
`//registry.npmjs.org/:_authToken=${token}`
);
}
function getNextVersion(packageJson) {
function getNextVersion(packageJson: {
dependencies?: { [key: string]: string };
devDependencies?: { [key: string]: string };
}) {
let nextVersion;
if (packageJson.dependencies && packageJson.dependencies.next) {
nextVersion = packageJson.dependencies.next;
@@ -94,7 +96,7 @@ function getNextVersion(packageJson) {
return nextVersion;
}
function isLegacyNext(nextVersion) {
function isLegacyNext(nextVersion: string) {
// If version is using the dist-tag instead of a version range
if (nextVersion === 'canary' || nextVersion === 'latest') {
return false;
@@ -114,73 +116,95 @@ function isLegacyNext(nextVersion) {
return true;
}
function setNextExperimentalPage(files, entry, meta) {
if (meta.requestPath) {
if (meta.requestPath.startsWith(path.join(entry, 'static'))) {
return onlyStaticDirectory(
includeOnlyEntryDirectory(files, entry),
entry,
);
}
const name = '[@now/next]';
const urls: stringMap = {};
const { pathname } = url.parse(meta.requestPath);
const assetPath = pathname.match(
/^\/?_next\/static\/[^/]+\/pages\/(.+)\.js$/,
function startDevServer(entryPath: string) {
const forked = fork(path.join(__dirname, 'dev-server.js'), [], {
cwd: entryPath,
execArgv: [],
env: {
NOW_REGION: 'dev1',
},
});
const getUrl = () =>
new Promise<string>((resolve, reject) => {
forked.on('message', resolve);
forked.on('error', reject);
});
return { forked, getUrl };
}
export const config = {
maxLambdaSize: '5mb',
};
export const build = async ({
files,
workPath,
entrypoint,
meta = {} as BuildParamsMeta,
}: BuildParamsType): Promise<{
routes?: any[];
output: Files;
watch?: string[];
childProcesses: ChildProcess[];
}> => {
validateEntrypoint(entrypoint);
const routes: any[] = [];
const entryDirectory = path.dirname(entrypoint);
const entryPath = path.join(workPath, entryDirectory);
const dotNext = path.join(entryPath, '.next');
console.log(`${name} Downloading user files...`);
await download(files, workPath, meta);
const pkg = await readPackageJson(entryPath);
const nextVersion = getNextVersion(pkg);
if (!nextVersion) {
throw new Error(
'No Next.js version could be detected in "package.json". Make sure `"next"` is installed in "dependencies" or "devDependencies"'
);
// eslint-disable-next-line no-underscore-dangle
process.env.__NEXT_BUILDER_EXPERIMENTAL_PAGE = assetPath
? assetPath[1]
: pathname;
}
process.env.__NEXT_BUILDER_EXPERIMENTAL_TARGET = 'serverless';
if (meta.isDev) {
// eslint-disable-next-line no-underscore-dangle
process.env.__NEXT_BUILDER_EXPERIMENTAL_DEBUG = 'true';
}
let childProcess: ChildProcess | undefined;
return null;
}
exports.config = {
maxLambdaSize: '5mb',
};
/**
* @param {BuildParamsType} buildParams
* @returns {Promise<Files>}
*/
exports.build = async ({
files, workPath, entrypoint, meta = {},
}) => {
validateEntrypoint(entrypoint);
const entryDirectory = path.dirname(entrypoint);
const maybeStaticFiles = setNextExperimentalPage(files, entryDirectory, meta);
if (maybeStaticFiles) return maybeStaticFiles; // return early if requestPath is static file
console.log('downloading user files...');
await download(files, workPath);
const entryPath = path.join(workPath, entryDirectory);
const dotNext = path.join(entryPath, '.next');
if (await pathExists(dotNext)) {
if (meta.isDev) {
await removePath(dotNext).catch((e) => {
if (e.code !== 'ENOENT') throw e;
});
} else {
console.warn(
'WARNING: You should probably not upload the `.next` directory. See https://zeit.co/docs/v2/deployments/official-builders/next-js-now-next/ for more information.',
// If this is the initial build, we want to start the server
if (!urls[entrypoint]) {
console.log(`${name} Installing dependencies...`);
await runNpmInstall(entryPath, ['--prefer-offline']);
const { forked, getUrl } = startDevServer(entryPath);
urls[entrypoint] = await getUrl();
childProcess = forked;
console.log(
`${name} Development server for ${entrypoint} running at ${
urls[entrypoint]
}`
);
}
const pathsInside = getPathsInside(entryDirectory, files);
return {
output: {},
routes: getRoutes(entryDirectory, pathsInside, files, urls[entrypoint]),
watch: pathsInside,
childProcesses: childProcess ? [childProcess] : [],
};
}
const pkg = await readPackageJson(entryPath);
let nextVersion = getNextVersion(pkg);
if (!nextVersion) {
throw new Error(
'No Next.js version could be detected in "package.json". Make sure `"next"` is installed in "dependencies" or "devDependencies"',
if (await pathExists(dotNext)) {
console.warn(
'WARNING: You should not upload the `.next` directory. See https://zeit.co/docs/v2/deployments/official-builders/next-js-now-next/ for more details.'
);
}
@@ -202,7 +226,7 @@ exports.build = async ({
}
console.warn(
"WARNING: your application is being deployed in @now/next's legacy mode. http://err.sh/zeit/now-builders/now-next-legacy-mode",
"WARNING: your application is being deployed in @now/next's legacy mode. http://err.sh/zeit/now-builders/now-next-legacy-mode"
);
console.log('normalizing package.json');
@@ -211,7 +235,7 @@ exports.build = async ({
await writePackageJson(entryPath, packageJson);
} else if (!pkg.scripts || !pkg.scripts['now-build']) {
console.warn(
'WARNING: "now-build" script not found. Adding \'"now-build": "next build"\' to "package.json" automatically',
'WARNING: "now-build" script not found. Adding \'"now-build": "next build"\' to "package.json" automatically'
);
pkg.scripts = {
'now-build': 'next build',
@@ -229,30 +253,14 @@ exports.build = async ({
console.log('installing dependencies...');
await runNpmInstall(entryPath, ['--prefer-offline']);
nextVersion = JSON.parse(
await readFile(resolveFrom(entryPath, 'next/package.json'), 'utf8'),
).version;
const isUpdated = (v) => {
if (v === 'canary') return true;
try {
return semver.satisfies(v, '>=8.0.5-canary.14', {
includePrerelease: true,
});
} catch (e) {
return false;
}
};
if ((meta.isDev || meta.requestPath) && !isUpdated(nextVersion)) {
throw new Error(
'`now dev` can only be used with Next.js >=8.0.5-canary.14!',
);
}
console.log('running user script...');
await runPackageJsonScript(entryPath, 'now-build');
const memoryToConsume = Math.floor(os.totalmem() / 1024 ** 2) - 128;
await runPackageJsonScript(entryPath, 'now-build', {
env: {
...process.env,
NODE_OPTIONS: `--max_old_space_size=${memoryToConsume}`,
},
} as SpawnOptions);
if (isLegacy) {
console.log('running npm install --production...');
@@ -263,21 +271,21 @@ exports.build = async ({
await unlinkFile(path.join(entryPath, '.npmrc'));
}
const lambdas = {};
const lambdas: { [key: string]: Lambda } = {};
if (isLegacy) {
const filesAfterBuild = await glob('**', entryPath);
console.log('preparing lambda files...');
let buildId;
let buildId: string;
try {
buildId = await readFile(
path.join(entryPath, '.next', 'BUILD_ID'),
'utf8',
'utf8'
);
} catch (err) {
console.error(
'BUILD_ID not found in ".next". The "package.json" "build" script did not run "next build"',
'BUILD_ID not found in ".next". The "package.json" "build" script did not run "next build"'
);
throw new Error('Missing BUILD_ID');
}
@@ -285,12 +293,12 @@ exports.build = async ({
const dotNextServerRootFiles = await glob('.next/server/*', entryPath);
const nodeModules = excludeFiles(
await glob('node_modules/**', entryPath),
file => file.startsWith('node_modules/.cache'),
file => file.startsWith('node_modules/.cache')
);
const launcherFiles = {
'now__bridge.js': new FileFsRef({ fsPath: require('@now/node-bridge') }),
};
const nextFiles = {
const nextFiles: { [key: string]: FileFsRef } = {
...nodeModules,
...dotNextRootFiles,
...dotNextServerRootFiles,
@@ -301,13 +309,13 @@ exports.build = async ({
}
const pages = await glob(
'**/*.js',
path.join(entryPath, '.next', 'server', 'static', buildId, 'pages'),
path.join(entryPath, '.next', 'server', 'static', buildId, 'pages')
);
const launcherPath = path.join(__dirname, 'legacy-launcher.js');
const launcherData = await readFile(launcherPath, 'utf8');
await Promise.all(
Object.keys(pages).map(async (page) => {
Object.keys(pages).map(async page => {
// These default pages don't have to be handled as they'd always 404
if (['_app.js', '_error.js', '_document.js'].includes(page)) {
return;
@@ -316,7 +324,7 @@ exports.build = async ({
const pathname = page.replace(/\.js$/, '');
const launcher = launcherData.replace(
'PATHNAME_PLACEHOLDER',
`/${pathname.replace(/(^|\/)index$/, '')}`,
`/${pathname.replace(/(^|\/)index$/, '')}`
);
const pageFiles = {
@@ -345,7 +353,7 @@ exports.build = async ({
runtime: 'nodejs8.10',
});
console.log(`Created lambda for page: "${page}"`);
}),
})
);
} else {
console.log('preparing lambda files...');
@@ -357,7 +365,7 @@ exports.build = async ({
};
const pages = await glob(
'**/*.js',
path.join(entryPath, '.next', 'serverless', 'pages'),
path.join(entryPath, '.next', 'serverless', 'pages')
);
const pageKeys = Object.keys(pages);
@@ -372,14 +380,14 @@ exports.build = async ({
}
throw new Error(
'No serverless pages were built. https://err.sh/zeit/now-builders/now-next-no-serverless-pages-built',
'No serverless pages were built. https://err.sh/zeit/now-builders/now-next-no-serverless-pages-built'
);
}
// An optional assets folder that is placed alongside every page entrypoint
const assets = await glob(
'assets/**',
path.join(entryPath, '.next', 'serverless'),
path.join(entryPath, '.next', 'serverless')
);
const assetKeys = Object.keys(assets);
@@ -389,7 +397,7 @@ exports.build = async ({
}
await Promise.all(
pageKeys.map(async (page) => {
pageKeys.map(async page => {
// These default pages don't have to be handled as they'd always 404
if (['_app.js', '_error.js', '_document.js'].includes(page)) {
return;
@@ -408,37 +416,48 @@ exports.build = async ({
runtime: 'nodejs8.10',
});
console.log(`Created lambda for page: "${page}"`);
}),
})
);
}
const nextStaticFiles = await glob(
'**',
path.join(entryPath, '.next', 'static'),
path.join(entryPath, '.next', 'static')
);
const staticFiles = Object.keys(nextStaticFiles).reduce(
(mappedFiles, file) => ({
...mappedFiles,
[path.join(entryDirectory, `_next/static/${file}`)]: nextStaticFiles[file],
[path.join(entryDirectory, `_next/static/${file}`)]: nextStaticFiles[
file
],
}),
{},
{}
);
const staticDirectoryFiles = onlyStaticDirectory(
includeOnlyEntryDirectory(files, entryDirectory),
entryDirectory,
entryDirectory
);
return { ...lambdas, ...staticFiles, ...staticDirectoryFiles };
return {
output: { ...lambdas, ...staticFiles, ...staticDirectoryFiles },
routes: [],
watch: [],
childProcesses: [],
};
};
exports.prepareCache = async ({ workPath, entrypoint }) => {
export const prepareCache = async ({
workPath,
entrypoint,
}: PrepareCacheOptions) => {
console.log('preparing cache ...');
const entryDirectory = path.dirname(entrypoint);
const entryPath = path.join(workPath, entryDirectory);
const pkg = await readPackageJson(entryPath);
const nextVersion = getNextVersion(pkg);
if (!nextVersion) throw new Error('Could not parse Next.js version');
const isLegacy = isLegacyNext(nextVersion);
if (isLegacy) {
@@ -448,29 +467,12 @@ exports.prepareCache = async ({ workPath, entrypoint }) => {
console.log('producing cache file manifest ...');
const cacheEntrypoint = path.relative(workPath, entryPath);
return {
const cache = {
...(await glob(path.join(cacheEntrypoint, 'node_modules/**'), workPath)),
...(await glob(path.join(cacheEntrypoint, '.next/cache/**'), workPath)),
...(await glob(path.join(cacheEntrypoint, 'package-lock.json'), workPath)),
...(await glob(path.join(cacheEntrypoint, 'yarn.lock'), workPath)),
};
};
exports.subscribe = async ({ entrypoint, files }) => {
const entryDirectory = path.dirname(entrypoint);
const pageFiles = includeOnlyEntryDirectory(
files,
path.join(entryDirectory, 'pages'),
);
return [
path.join(entryDirectory, '_next/static/unoptimized-build/pages/**'),
path.join(entryDirectory, 'static/**'),
// List all pages without their extensions
...Object.keys(pageFiles).map(page => page
.replace(/^pages\//i, '')
.split('.')
.slice(0, -1)
.join('.')),
];
console.log('cache file manifest produced');
return cache;
};

View File

@@ -1,7 +1,7 @@
const { Server } = require('http');
const next = require('next-server');
const url = require('url');
const { Bridge } = require('./now__bridge');
import { Server } from 'http';
import next from 'next-server';
import url from 'url';
import { Bridge } from './now__bridge';
if (!process.env.NODE_ENV) {
process.env.NODE_ENV = process.env.NOW_REGION === 'dev1' ? 'development' : 'production';
@@ -10,7 +10,7 @@ if (!process.env.NODE_ENV) {
const app = next({});
const server = new Server((req, res) => {
const parsedUrl = url.parse(req.url, true);
const parsedUrl = url.parse(req.url || '', true);
app.render(req, res, 'PATHNAME_PLACEHOLDER', parsedUrl.query, parsedUrl);
});

View File

@@ -1,4 +1,4 @@
module.exports = [
export default [
'0.1.0',
'0.1.1',
'0.2.0',

View File

@@ -1,16 +1,13 @@
const fs = require('fs-extra');
const path = require('path');
import fs from 'fs-extra';
import path from 'path';
import { Files } from '@now/build-utils';
/** @typedef { import('@now/build-utils/file-ref') } FileRef */
/** @typedef { import('@now/build-utils/file-fs-ref') } FileFsRef */
/** @typedef {{[filePath: string]: FileRef|FileFsRef}} Files */
type stringMap = {[key: string]: string};
/**
* Validate if the entrypoint is allowed to be used
* @param {string} entrypoint
* @throws {Error}
*/
function validateEntrypoint(entrypoint) {
function validateEntrypoint(entrypoint: string) {
if (
!/package\.json$/.exec(entrypoint)
&& !/next\.config\.js$/.exec(entrypoint)
@@ -21,21 +18,10 @@ function validateEntrypoint(entrypoint) {
}
}
/**
* This callback type is called `requestCallback` and is displayed as a global symbol.
*
* @callback matcher
* @param {string} filePath
* @returns {boolean}
*/
/**
* Exclude certain files from the files object
* @param {Files} files
* @param {matcher} matcher
* @returns {Files}
*/
function excludeFiles(files, matcher) {
function excludeFiles(files: Files, matcher: (filePath: string) => boolean): Files {
return Object.keys(files).reduce((newFiles, filePath) => {
if (matcher(filePath)) {
return newFiles;
@@ -49,16 +35,13 @@ function excludeFiles(files, matcher) {
/**
* Creates a new Files object holding only the entrypoint files
* @param {Files} files
* @param {string} entryDirectory
* @returns {Files}
*/
function includeOnlyEntryDirectory(files, entryDirectory) {
function includeOnlyEntryDirectory(files: Files, entryDirectory: string): Files {
if (entryDirectory === '.') {
return files;
}
function matcher(filePath) {
function matcher(filePath: string) {
return !filePath.startsWith(entryDirectory);
}
@@ -67,10 +50,8 @@ function includeOnlyEntryDirectory(files, entryDirectory) {
/**
* Exclude package manager lockfiles from files
* @param {Files} files
* @returns {Files}
*/
function excludeLockFiles(files) {
function excludeLockFiles(files: Files): Files {
const newFiles = files;
if (newFiles['package-lock.json']) {
delete newFiles['package-lock.json'];
@@ -83,11 +64,9 @@ function excludeLockFiles(files) {
/**
* Include the static directory from files
* @param {Files} files
* @returns {Files}
*/
function onlyStaticDirectory(files, entryDir) {
function matcher(filePath) {
function onlyStaticDirectory(files: Files, entryDir: string): Files {
function matcher(filePath: string) {
return !filePath.startsWith(path.join(entryDir, 'static'));
}
@@ -96,11 +75,10 @@ function onlyStaticDirectory(files, entryDir) {
/**
* Enforce specific package.json configuration for smallest possible lambda
* @param {{dependencies?: any, devDependencies?: any, scripts?: any}} defaultPackageJson
*/
function normalizePackageJson(defaultPackageJson = {}) {
const dependencies = {};
const devDependencies = {
function normalizePackageJson(defaultPackageJson: {dependencies?: stringMap, devDependencies?: stringMap, scripts?: stringMap} = {}) {
const dependencies: stringMap = {};
const devDependencies: stringMap = {
...defaultPackageJson.dependencies,
...defaultPackageJson.devDependencies,
};
@@ -139,7 +117,7 @@ function normalizePackageJson(defaultPackageJson = {}) {
};
}
async function getNextConfig(workPath, entryPath) {
async function getNextConfig(workPath: string, entryPath: string) {
const entryConfig = path.join(entryPath, './next.config.js');
if (await fs.pathExists(entryConfig)) {
return fs.readFile(entryConfig, 'utf8');
@@ -153,7 +131,84 @@ async function getNextConfig(workPath, entryPath) {
return null;
}
module.exports = {
function pathIsInside(firstPath: string, secondPath: string) {
return !path.relative(firstPath, secondPath).startsWith('..');
}
function getPathsInside(entryDirectory: string, files: Files) {
const watch: string[] = [];
for (const file of Object.keys(files)) {
// If the file is outside of the entrypoint directory, we do
// not want to monitor it for changes.
if (!pathIsInside(entryDirectory, file)) {
continue;
}
watch.push(file);
}
return watch;
}
function getRoutes(entryDirectory: string, pathsInside: string[], files: Files, url: string): any[] {
const filesInside: Files = {};
const prefix = entryDirectory === `.` ? `/` : `/${entryDirectory}/`;
for (const file of Object.keys(files)) {
if (!pathsInside.includes(file)) {
continue;
}
filesInside[file] = files[file];
}
const routes: any[] = [
{
src: `${prefix}_next/(.*)`,
dest: `${url}/_next/$1`
},
{
src: `${prefix}static/(.*)`,
dest: `${url}/static/$1`
}
];
for (const file of Object.keys(filesInside)) {
const relativePath = path.relative(entryDirectory, file);
const isPage = pathIsInside('pages', relativePath);
if (!isPage) {
continue;
}
const relativeToPages = path.relative('pages', relativePath);
const extension = path.extname(relativeToPages);
const pageName = relativeToPages.replace(extension, '');
if (pageName.startsWith('_')) {
continue;
}
routes.push({
src: `${prefix}${pageName}`,
dest: `${url}/${pageName}`
});
if (pageName.endsWith('index')) {
const resolvedIndex = pageName.replace('/index', '').replace('index', '');
routes.push({
src: `${prefix}${resolvedIndex}`,
dest: `${url}/${resolvedIndex}`
});
}
}
return routes;
}
export {
excludeFiles,
validateEntrypoint,
includeOnlyEntryDirectory,
@@ -161,4 +216,7 @@ module.exports = {
normalizePackageJson,
onlyStaticDirectory,
getNextConfig,
getPathsInside,
getRoutes,
stringMap,
};

View File

@@ -1,17 +1,17 @@
/* global it, expect */
const path = require('path');
const runBuildLambda = require('../../lib/run-build-lambda');
const runBuildLambda = require('../../../../test/lib/run-build-lambda');
const FOUR_MINUTES = 240000;
it(
'Should build the standard example',
async () => {
const { buildResult } = await runBuildLambda(
path.join(__dirname, 'standard'),
);
expect(buildResult.index).toBeDefined();
const filePaths = Object.keys(buildResult);
const {
buildResult: { output },
} = await runBuildLambda(path.join(__dirname, 'standard'));
expect(output.index).toBeDefined();
const filePaths = Object.keys(output);
const hasUnderScoreAppStaticFile = filePaths.some(filePath => filePath.match(/static.*\/pages\/_app\.js$/));
const hasUnderScoreErrorStaticFile = filePaths.some(filePath => filePath.match(/static.*\/pages\/_error\.js$/));
expect(hasUnderScoreAppStaticFile).toBeTruthy();
@@ -23,12 +23,12 @@ it(
it(
'Should build the monorepo example',
async () => {
const { buildResult } = await runBuildLambda(
path.join(__dirname, 'monorepo'),
);
expect(buildResult['www/index']).toBeDefined();
expect(buildResult['www/static/test.txt']).toBeDefined();
const filePaths = Object.keys(buildResult);
const {
buildResult: { output },
} = await runBuildLambda(path.join(__dirname, 'monorepo'));
expect(output['www/index']).toBeDefined();
expect(output['www/static/test.txt']).toBeDefined();
const filePaths = Object.keys(output);
const hasUnderScoreAppStaticFile = filePaths.some(filePath => filePath.match(/static.*\/pages\/_app\.js$/));
const hasUnderScoreErrorStaticFile = filePaths.some(filePath => filePath.match(/static.*\/pages\/_error\.js$/));
expect(hasUnderScoreAppStaticFile).toBeTruthy();
@@ -40,11 +40,11 @@ it(
it(
'Should build the legacy standard example',
async () => {
const { buildResult } = await runBuildLambda(
path.join(__dirname, 'legacy-standard'),
);
expect(buildResult.index).toBeDefined();
const filePaths = Object.keys(buildResult);
const {
buildResult: { output },
} = await runBuildLambda(path.join(__dirname, 'legacy-standard'));
expect(output.index).toBeDefined();
const filePaths = Object.keys(output);
const hasUnderScoreAppStaticFile = filePaths.some(filePath => filePath.match(/static.*\/pages\/_app\.js$/));
const hasUnderScoreErrorStaticFile = filePaths.some(filePath => filePath.match(/static.*\/pages\/_error\.js$/));
expect(hasUnderScoreAppStaticFile).toBeTruthy();
@@ -56,10 +56,10 @@ it(
it(
'Should build the legacy custom dependency test',
async () => {
const { buildResult } = await runBuildLambda(
path.join(__dirname, 'legacy-custom-dependency'),
);
expect(buildResult.index).toBeDefined();
const {
buildResult: { output },
} = await runBuildLambda(path.join(__dirname, 'legacy-custom-dependency'));
expect(output.index).toBeDefined();
},
FOUR_MINUTES,
);
@@ -78,10 +78,10 @@ it('Should throw when package.json or next.config.js is not the "src"', async ()
it(
'Should build the static-files test on legacy',
async () => {
const { buildResult } = await runBuildLambda(
path.join(__dirname, 'legacy-static-files'),
);
expect(buildResult['static/test.txt']).toBeDefined();
const {
buildResult: { output },
} = await runBuildLambda(path.join(__dirname, 'legacy-static-files'));
expect(output['static/test.txt']).toBeDefined();
},
FOUR_MINUTES,
);
@@ -89,10 +89,10 @@ it(
it(
'Should build the static-files test',
async () => {
const { buildResult } = await runBuildLambda(
path.join(__dirname, 'static-files'),
);
expect(buildResult['static/test.txt']).toBeDefined();
const {
buildResult: { output },
} = await runBuildLambda(path.join(__dirname, 'static-files'));
expect(output['static/test.txt']).toBeDefined();
},
FOUR_MINUTES,
);

View File

@@ -0,0 +1,90 @@
/* global expect, it, jest */
const path = require('path');
const os = require('os');
const { build } = require('@now/next');
const { FileBlob } = require('@now/build-utils');
jest.setTimeout(45000);
describe('build meta dev', () => {
const files = {
'next.config.js': new FileBlob({
mode: 0o777,
data: `
module.exports = {
target: 'serverless'
}
`,
}),
'pages/index.js': new FileBlob({
mode: 0o777,
data: `
export default () => 'Index page'
`,
}),
'package.json': new FileBlob({
mode: 0o777,
data: `
{
"scripts": {
"now-build": "next build"
},
"dependencies": {
"next": "8",
"react": "16",
"react-dom": "16"
}
}
`,
}),
};
const entrypoint = 'next.config.js';
const workPath = path.join(
os.tmpdir(),
Math.random()
.toString()
.slice(3),
);
console.log('workPath directory: ', workPath);
/*
it('should have builder v2 response isDev=false', async () => {
const meta = { isDev: false, requestPath: null };
const { output, routes, watch } = await build({
files,
workPath,
entrypoint,
meta,
});
//console.log('output: ', Object.keys(output));
expect(Object.keys(output).length).toBe(7);
expect(output.index.type).toBe('Lambda');
expect(routes.length).toBe(0);
expect(watch.length).toBe(0);
});
*/
it('should have builder v2 response isDev=true', async () => {
const meta = { isDev: true, requestPath: null };
const {
output, routes, watch, childProcesses,
} = await build({
files,
workPath,
entrypoint,
meta,
});
routes.forEach((route) => {
// eslint-disable-next-line no-param-reassign
route.dest = route.dest.replace(':4000', ':5000');
});
expect(output).toEqual({});
expect(routes).toEqual([
{ src: '/_next/(.*)', dest: 'http://localhost:5000/_next/$1' },
{ src: '/static/(.*)', dest: 'http://localhost:5000/static/$1' },
{ src: '/index', dest: 'http://localhost:5000/index' },
{ src: '/', dest: 'http://localhost:5000/' },
]);
expect(watch).toEqual(['next.config.js', 'pages/index.js', 'package.json']);
childProcesses.forEach(cp => cp.kill());
});
});

View File

@@ -5,7 +5,7 @@ const {
includeOnlyEntryDirectory,
normalizePackageJson,
getNextConfig,
} = require('@now/next/utils');
} = require('@now/next/dist/utils');
const FileRef = require('@now/build-utils/file-ref'); // eslint-disable-line import/no-extraneous-dependencies
describe('getNextConfig', () => {

View File

@@ -1,20 +1,18 @@
{
"compilerOptions": {
"target": "ES2017",
"strict": true,
"esModuleInterop": true,
"lib": ["esnext"],
"target": "esnext",
"module": "commonjs",
"lib": ["es2017"],
"allowJs": true,
"checkJs": true,
"noEmit": true,
"strict": false,
"types": ["node"],
"esModuleInterop": true
"outDir": "dist",
"sourceMap": false,
"declaration": false
},
"include": [
"./"
"src/**/*"
],
"exclude": [
"./launcher.js",
"./legacy-launcher.js"
"node_modules"
]
}

View File

@@ -1,6 +1,6 @@
{
"name": "@now/node-bridge",
"version": "1.0.2-canary.2",
"version": "1.1.0",
"license": "MIT",
"main": "./index.js",
"repository": {

View File

@@ -43,7 +43,10 @@ async function compile(workPath, downloadedFiles, entrypoint, config) {
const input = downloadedFiles[entrypoint].fsPath;
const inputDir = path.dirname(input);
const ncc = require('@zeit/ncc');
const { code, assets } = await ncc(input, { sourceMap: true });
const { code, map, assets } = await ncc(input, {
sourceMap: true,
sourceMapRegister: true,
});
if (config && config.includeFiles) {
// eslint-disable-next-line no-restricted-syntax
@@ -67,16 +70,16 @@ async function compile(workPath, downloadedFiles, entrypoint, config) {
}
const preparedFiles = {};
const blob = new FileBlob({ data: code });
// move all user code to 'user' subdirectory
preparedFiles[entrypoint] = blob;
preparedFiles[entrypoint] = new FileBlob({ data: code });
preparedFiles[`${entrypoint.replace('.ts', '.js')}.map`] = new FileBlob({
data: map,
});
// eslint-disable-next-line no-restricted-syntax
for (const assetName of Object.keys(assets)) {
const { source: data, permissions: mode } = assets[assetName];
const blob2 = new FileBlob({ data, mode });
preparedFiles[
path.join(path.dirname(entrypoint), assetName)
] = blob2;
preparedFiles[path.join(path.dirname(entrypoint), assetName)] = blob2;
}
return preparedFiles;
@@ -93,10 +96,7 @@ exports.config = {
exports.build = async ({
files, entrypoint, config, workPath,
}) => {
const [
downloadedFiles,
entrypointFsDirname,
] = await downloadInstallAndBundle(
const [downloadedFiles, entrypointFsDirname] = await downloadInstallAndBundle(
{ files, entrypoint, workPath },
{ npmArguments: ['--prefer-offline'] },
);
@@ -123,9 +123,7 @@ exports.build = async ({
let launcherData = await fs.readFile(launcherPath, 'utf8');
launcherData = launcherData.replace(
'// PLACEHOLDER',
[
`require("./${entrypoint}");`,
].join(' '),
[`require("./${entrypoint}");`].join(' '),
);
const launcherFiles = {

View File

@@ -1,6 +1,6 @@
{
"name": "@now/node-server",
"version": "0.5.4-canary.3",
"version": "0.6.0",
"license": "MIT",
"repository": {
"type": "git",
@@ -8,8 +8,8 @@
"directory": "packages/now-node-server"
},
"dependencies": {
"@now/node-bridge": "^1.0.2-canary.2",
"@zeit/ncc": "0.17.3",
"@now/node-bridge": "^1.1.0",
"@zeit/ncc": "0.18.1",
"fs-extra": "7.0.1"
},
"scripts": {

View File

@@ -1,6 +1,6 @@
{
"name": "@now/node",
"version": "0.5.4-canary.3",
"version": "0.6.0",
"license": "MIT",
"main": "./dist/index",
"repository": {
@@ -9,8 +9,8 @@
"directory": "packages/now-node"
},
"dependencies": {
"@now/node-bridge": "^1.0.2-canary.2",
"@zeit/ncc": "0.17.3",
"@now/node-bridge": "^1.1.0",
"@zeit/ncc": "0.18.1",
"fs-extra": "7.0.1"
},
"scripts": {

View File

@@ -1,5 +1,5 @@
import { join, dirname } from 'path';
import { remove, readFile } from 'fs-extra';
import { join, dirname, sep } from 'path';
import { readFile } from 'fs-extra';
import {
glob,
download,
@@ -44,8 +44,9 @@ async function downloadInstallAndBundle({
async function compile(entrypointPath: string, entrypoint: string, config: CompilerConfig): Promise<Files> {
const input = entrypointPath;
const inputDir = dirname(input);
const rootIncludeFiles = inputDir.split(sep).pop() || '';
const ncc = require('@zeit/ncc');
const { code, assets } = await ncc(input);
const { code, map, assets } = await ncc(input, { sourceMap: true, sourceMapRegister: true });
if (config && config.includeFiles) {
for (const pattern of config.includeFiles) {
@@ -55,8 +56,15 @@ async function compile(entrypointPath: string, entrypoint: string, config: Compi
const stream = files[assetName].toStream();
const { mode } = files[assetName];
const { data } = await FileBlob.fromStream({ stream });
let fullPath = join(rootIncludeFiles, assetName);
assets[assetName] = {
// if asset contain directory
// no need to use `rootIncludeFiles`
if (assetName.includes(sep)) {
fullPath = assetName
}
assets[fullPath] = {
'source': data,
'permissions': mode
};
@@ -65,9 +73,9 @@ async function compile(entrypointPath: string, entrypoint: string, config: Compi
}
const preparedFiles: Files = {};
const blob = new FileBlob({ data: code });
// move all user code to 'user' subdirectory
preparedFiles[entrypoint] = blob;
preparedFiles[entrypoint] = new FileBlob({ data: code });
preparedFiles[`${entrypoint.replace('.ts', '.js')}.map`] = new FileBlob({ data: map });
// eslint-disable-next-line no-restricted-syntax
for (const assetName of Object.keys(assets)) {
const { source: data, permissions: mode } = assets[assetName];

View File

@@ -9,12 +9,25 @@
"templates/**"
]
}
},
{
"src": "root.js",
"use": "@now/node",
"config": {
"includeFiles": [
"root.edge"
]
}
}
],
"probes": [
{
"path": "/",
"mustContain": "hello Now!"
},
{
"path": "/root.js",
"mustContain": "hello Root!"
}
]
}

View File

@@ -0,0 +1 @@
hello {{ text }}

View File

@@ -0,0 +1,7 @@
const edge = require('edge.js');
module.exports = (req, resp) => {
edge.registerViews(__dirname);
resp.end(edge.render('root', { text: 'Root!' }));
};

View File

@@ -0,0 +1,10 @@
module.exports = (req, res) => {
try {
if (req) {
throw new Error(`Should throw ${process.env.RANDOMNESS_ENV_VAR}`);
}
res.end(`Should not print ${process.env.RANDOMNESS_ENV_VAR}`);
} catch (error) {
res.end(error.stack);
}
};

View File

@@ -0,0 +1,12 @@
{
"version": 2,
"builds": [
{ "src": "index.js", "use": "@now/node" }
],
"probes": [
{
"path": "/",
"mustContain": "index.js:4"
}
]
}

View File

@@ -0,0 +1,10 @@
export default function handler (req: any, res: any) {
try {
if (req) {
throw new Error(`Should throw`);
}
res.end(`Should not print`);
} catch (error) {
res.end(error.stack);
}
}

View File

@@ -0,0 +1,12 @@
{
"version": 2,
"builds": [
{ "src": "index.ts", "use": "@now/node" }
],
"probes": [
{
"path": "/",
"mustContain": "index.ts:4"
}
]
}

View File

@@ -0,0 +1,14 @@
{
"compilerOptions": {
"strict": true,
"esModuleInterop": true,
"sourceMap": true,
"lib": ["esnext"],
"target": "esnext",
"module": "commonjs"
},
"include": [
"index.ts"
]
}

2
packages/now-optipng/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
# build output
/dist

View File

@@ -1,15 +0,0 @@
const FileBlob = require('@now/build-utils/file-blob.js'); // eslint-disable-line import/no-extraneous-dependencies
const OptiPng = require('optipng');
const pipe = require('multipipe');
exports.analyze = ({ files, entrypoint }) => files[entrypoint].digest;
exports.build = async ({ files, entrypoint }) => {
const optimizer = new OptiPng(['-o9']);
const stream = pipe(
files[entrypoint].toStream(),
optimizer,
);
const result = await FileBlob.fromStream({ stream });
return { [entrypoint]: result };
};

View File

@@ -1,7 +1,15 @@
{
"name": "@now/optipng",
"version": "0.4.9-canary.0",
"version": "0.5.0",
"license": "MIT",
"main": "./dist/index",
"files": [
"dist"
],
"scripts": {
"build": "tsc",
"test": "tsc && jest"
},
"repository": {
"type": "git",
"url": "https://github.com/zeit/now-builders.git",
@@ -10,5 +18,9 @@
"dependencies": {
"multipipe": "2.0.3",
"optipng": "1.1.0"
},
"devDependencies": {
"@types/node": "11.9.4",
"typescript": "3.3.3"
}
}

View File

@@ -0,0 +1,22 @@
// eslint-disable-line import/no-extraneous-dependencies
import {
FileBlob,
BuildOptions,
AnalyzeOptions
} from '@now/build-utils'
import OptiPng from 'optipng'
import pipe from 'multipipe'
export function analyze({ files, entrypoint }: AnalyzeOptions) {
return files[entrypoint].digest;
}
export async function build({ files, entrypoint }: BuildOptions) {
const optimizer = new OptiPng(['-o9']);
const stream = pipe(
files[entrypoint].toStream(),
optimizer
);
const result = await FileBlob.fromStream({ stream });
return { [entrypoint]: result };
}

View File

@@ -0,0 +1,11 @@
{
"version": 2,
"builds": [
{ "src": "showcase.png", "use": "@now/optipng" },
{ "src": "subdirectory/naruto.png", "use": "@now/optipng" }
],
"probes": [
{ "path": "/", "mustContain": "showcase.png" },
{ "path": "/", "mustContain": "subdirectory" }
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 792 KiB

View File

@@ -0,0 +1,33 @@
/* global beforeAll, expect, it, jest */
const fs = require('fs');
const path = require('path');
const {
packAndDeploy,
testDeployment,
} = require('../../../test/lib/deployment/test-deployment.js');
jest.setTimeout(4 * 60 * 1000);
const buildUtilsUrl = '@canary';
let builderUrl;
beforeAll(async () => {
const builderPath = path.resolve(__dirname, '..');
builderUrl = await packAndDeploy(builderPath);
console.log('builderUrl', builderUrl);
});
const fixturesPath = path.resolve(__dirname, 'fixtures');
// eslint-disable-next-line no-restricted-syntax
for (const fixture of fs.readdirSync(fixturesPath)) {
// eslint-disable-next-line no-loop-func
it(`should build ${fixture}`, async () => {
await expect(
testDeployment(
{ builderUrl, buildUtilsUrl },
path.join(fixturesPath, fixture),
),
).resolves.toBeDefined();
});
}

View File

@@ -0,0 +1,20 @@
{
"compilerOptions": {
"baseUrl": ".",
"paths": { "*": ["types/*"] },
"strict": true,
"esModuleInterop": true,
"lib": ["esnext"],
"target": "esnext",
"module": "commonjs",
"outDir": "dist",
"sourceMap": false,
"declaration": false
},
"include": [
"src/**/*"
],
"exclude": [
"node_modules"
]
}

View File

@@ -0,0 +1,7 @@
// Type definitios for [multipipe] [v0.0.1]
// Project: now-optipng
// Definitions by: [Markoz Peña Mendez]
export = pipe;
declare function pipe(...streams: any[]): NodeJS.ReadableStream;

10
packages/now-optipng/types/optipng.d.ts vendored Normal file
View File

@@ -0,0 +1,10 @@
// Type definitios for [optipng] [v0.0.1]
// Project: now-optipng
// Definitions by: [Markoz Peña Mendez]
export = OptiPng;
declare class OptiPng {
constructor(bindArgs: string[]);
bindArgs: string[];
}

View File

@@ -1,6 +1,6 @@
{
"name": "@now/php-bridge",
"version": "0.4.16-canary.0",
"version": "0.5.0",
"license": "MIT",
"repository": {
"type": "git",

View File

@@ -1,6 +1,6 @@
{
"name": "@now/php",
"version": "0.4.17-canary.1",
"version": "0.5.0",
"license": "MIT",
"repository": {
"type": "git",
@@ -8,7 +8,7 @@
"directory": "packages/now-php"
},
"dependencies": {
"@now/php-bridge": "^0.4.16-canary.0"
"@now/php-bridge": "^0.5.0"
},
"scripts": {
"test": "jest"

View File

@@ -1,6 +1,6 @@
{
"name": "@now/python",
"version": "0.1.0-canary.2",
"version": "0.1.0",
"main": "index.js",
"license": "MIT",
"repository": {

View File

@@ -1,6 +1,6 @@
{
"name": "@now/rust",
"version": "0.1.2-canary.2",
"version": "0.2.0",
"license": "MIT",
"repository": {
"type": "git",

View File

@@ -1,5 +1,9 @@
const path = require('path');
const { existsSync } = require('fs');
const getPort = require('get-port');
const { promisify } = require('util');
const { timeout } = require('promise-timeout');
const { existsSync, readFileSync } = require('fs');
const waitForPort = promisify(require('wait-for-port'));
const {
glob,
download,
@@ -17,12 +21,16 @@ function validateDistDir(distDir) {
}
}
exports.version = 2;
const nowDevScriptPorts = new Map();
exports.build = async ({
files, entrypoint, workPath, config,
files, entrypoint, workPath, config, meta = {},
}) => {
console.log('downloading user files...');
await download(files, workPath);
console.log('running user scripts...');
await download(files, workPath, meta);
const mountpoint = path.dirname(entrypoint);
const entrypointFsDirname = path.join(workPath, mountpoint);
const distPath = path.join(
@@ -31,13 +39,79 @@ exports.build = async ({
(config && config.distDir) || 'dist',
);
if (path.basename(entrypoint) === 'package.json') {
const entrypointName = path.basename(entrypoint);
if (entrypointName === 'package.json') {
await runNpmInstall(entrypointFsDirname, ['--prefer-offline']);
if (await runPackageJsonScript(entrypointFsDirname, 'now-build')) {
const pkgPath = path.join(workPath, entrypoint);
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
let output = {};
const routes = [];
if (meta.isDev && pkg.scripts && pkg.scripts['now-dev']) {
let devPort = nowDevScriptPorts.get(entrypoint);
if (typeof devPort === 'number') {
console.log('`now-dev` server already running for %j', entrypoint);
} else {
// Run the `now-dev` script out-of-bounds, since it is assumed that
// it will launch a dev server that never "completes"
devPort = await getPort();
nowDevScriptPorts.set(entrypoint, devPort);
const opts = {
env: { ...process.env, PORT: String(devPort) },
};
const promise = runPackageJsonScript(
entrypointFsDirname,
'now-dev',
opts,
);
promise.then(
() => {
nowDevScriptPorts.delete(entrypoint);
},
(err) => {
console.log('`now-dev` script error:', err);
nowDevScriptPorts.delete(entrypoint);
},
);
// Now wait for the server to have listened on `$PORT`, after which we
// will ProxyPass any requests to that development server that come in
// for this builder.
try {
await timeout(waitForPort('localhost', devPort), 60 * 1000);
} catch (err) {
throw new Error(
`Failed to detect a server running on port ${devPort}`,
);
}
console.log('Detected dev server for %j', entrypoint);
}
let srcBase = mountpoint.replace(/^\.\/?/, '');
if (srcBase.length > 0) {
srcBase = `/${srcBase}`;
}
routes.push({
src: `${srcBase}/(.*)`,
dest: `http://localhost:${devPort}${srcBase}/$1`,
});
} else {
// Run the `now-build` script and wait for completion to collect the build
// outputs
console.log('running user "now-build" script from `package.json`...');
if (!(await runPackageJsonScript(entrypointFsDirname, 'now-build'))) {
throw new Error(
`An error running "now-build" script in "${entrypoint}"`,
);
}
validateDistDir(distPath);
return glob('**', distPath, mountpoint);
output = await glob('**', distPath, mountpoint);
}
throw new Error(`An error running "now-build" script in "${entrypoint}"`);
const watch = [path.join(mountpoint.replace(/^\.\/?/, ''), '**/*')];
return { routes, watch, output };
}
if (path.extname(entrypoint) === '.sh') {

View File

@@ -1,6 +1,6 @@
{
"name": "@now/static-build",
"version": "0.4.19-canary.2",
"version": "0.5.0",
"license": "MIT",
"repository": {
"type": "git",
@@ -9,5 +9,10 @@
},
"scripts": {
"test": "jest"
},
"dependencies": {
"get-port": "5.0.0",
"promise-timeout": "1.3.0",
"wait-for-port": "0.0.2"
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@now/wordpress",
"version": "0.4.16-canary.0",
"version": "0.5.0",
"license": "MIT",
"repository": {
"type": "git",
@@ -8,7 +8,7 @@
"directory": "packages/now-wordpress"
},
"dependencies": {
"@now/php-bridge": "^0.4.16-canary.0",
"@now/php-bridge": "^0.5.0",
"node-fetch": "2.3.0",
"yauzl": "2.10.0"
},

View File

@@ -96,6 +96,8 @@ async function deploymentPost (payload) {
method: 'POST',
body: JSON.stringify(payload)
});
console.log(`fetch status: ${resp.status} ${resp.statusText}`);
const json = await resp.json();
if (json.error) {
@@ -125,8 +127,7 @@ async function fetchWithAuth (url, opts = {}) {
if (NOW_TOKEN) {
token = NOW_TOKEN;
} else if (NOW_TOKEN_FACTORY_URL) {
const resp = await fetch(NOW_TOKEN_FACTORY_URL);
token = (await resp.json()).token;
token = await fetchTokenWithRetry(NOW_TOKEN_FACTORY_URL);
} else {
const authJsonPath = path.join(homedir(), '.now/auth.json');
token = require(authJsonPath).token;
@@ -139,6 +140,27 @@ async function fetchWithAuth (url, opts = {}) {
return await fetchApi(url, opts);
}
function fetchTokenWithRetry (url, retries = 3) {
return new Promise(async (resolve, reject) => {
try {
const res = await fetch(url);
const data = await res.json();
resolve(data.token);
} catch (error) {
console.log(`Failed to fetch token. Retries remaining: ${retries}`);
if (retries === 0) {
reject(error);
return;
}
setTimeout(() => {
fetchTokenWithRetry(url, retries - 1)
.then(resolve)
.catch(reject);
}, 500);
}
});
}
async function fetchApi (url, opts = {}) {
const apiHost = process.env.API_HOST || 'api.zeit.co';
const urlWithHost = `https://${apiHost}${url}`;

View File

@@ -77,7 +77,7 @@ 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', deploymentUrl);
console.log('deploymentUrl', `https://${deploymentUrl}`);
for (const probe of nowJson.probes || []) {
console.log('testing', JSON.stringify(probe));

780
yarn.lock

File diff suppressed because it is too large Load Diff