Compare commits

..

60 Commits

Author SHA1 Message Date
Steven
0b54093ca2 Publish
- @now/go@0.5.7
 - @now/node@0.12.3
 - @now/python@0.2.13
 - @now/routing-utils@1.2.1
 - @now/rust@0.2.10
 - @now/static-build@0.9.3
2019-08-01 16:10:17 -07:00
Steven
8c7371e093 Revert "[now-build-utils][now-static-build] Fix spawn options fo… (#867)
This reverts commit cf640a619e35c8cebaf6655799f273ba11bbd6e6.
2019-08-01 16:06:00 -07:00
Matthew Sweeney
e59c0f59f5 [now-static-build] Add sapper Optimization and Test (#865)
* add sapper optimization and test

* remove test dir

* remove test scripts
2019-08-01 15:28:16 -07:00
Steven
21b115a788 Update CODEOWNERS (#866) 2019-08-01 15:28:07 -07:00
Connor Davis
c5df4f7f9e [now-routing-utils] Make Now Routing Utils a Public Library (#860)
* Make Now Routing Utils a Public Library

* Suggestions

* Cleanup

* Update lockfile

* Add to dist to eslint ignore

* v1.2.0-canary.0

* v1.2.0-canary.1

* v1.2.1-canary.0

* Use optional dep instead of peer dep
2019-08-01 15:28:02 -07:00
Igor Klopov
2429451115 [now-rust] install rust in npm install of the builder (#863) 2019-08-01 15:27:45 -07:00
Andy
21789cdbf1 [now-build-utils][now-static-build] Fix spawn options for windows (#864)
* [now-build-utils][now-static-build] Fix spawn options for windows

* Fix type

* Fix types
2019-08-01 15:27:39 -07:00
Igor Klopov
f74ed2aff1 [now-go] install golang in npm install step (#859)
* [now-go] install golang in npm install step

* update

* move logic into now-install script

* remove unneeded items from gitignore

* now-postinstall
2019-08-01 15:27:28 -07:00
Steven
2f83e6375a [now-node] Bump node-file-trace to 0.2.6 (#861) 2019-08-01 15:27:23 -07:00
Kai Richard König
99a7b8f1f7 [now-python] Use pipfile2req to convert pipfile to requirements.txt (#857)
* Use a more robust library to convert pipfile - closes #841

* Renaming for clarity

* Suppress warnings since we know it will be invoked manually

Co-Authored-By: Steven <steven@ceriously.com>

* Apply the flag while installing not while generating the requirements.

Co-Authored-By: Steven <steven@ceriously.com>
2019-08-01 15:27:17 -07:00
Steven
3fa7b80bde Publish
- @now/build-utils@0.9.6
 - @now/next@0.5.8
 - @now/node@0.12.2
2019-07-31 08:30:21 -04:00
Steven
d012e73166 [now-node] Bump node-file-trace to 0.2.5 (#852) 2019-07-31 08:29:09 -04:00
Andy
d6c46da37f [now-build-utils] Remove @now/nuxt from default builders (#850) 2019-07-31 08:29:04 -04:00
Andy
1508932ad6 [now-static-build] Allow setting the tag for zero-config builders (#842)
* [now-static-build] Allow to set the `tag` for zero-config builders

This reverts commit 32b6f1d6a9047920d6cc40385de711022f77c4d4.

* Remove unsupported builders

* Fix setting the tag
2019-07-31 08:28:58 -04:00
Steven
873b099f53 [now-node] Bump node-file-trace to 0.2.4 (#849) 2019-07-31 08:28:52 -04:00
Steven
6546d3b67d [now-build-utils] Fix require file path for legacy builders (#846)
* [now-build-utils] Fix require file path

* Add more tests
2019-07-31 08:28:46 -04:00
Steven
34ad4ac33a [now-next] Fix dev-server bundle output (#847)
* [now-next] Fix dev-server bundle

* [now-next] Fix bundle output dev-server
2019-07-31 08:28:39 -04:00
Steven
01d0b017af Publish
- @now/build-utils@0.9.5
 - @now/next@0.5.7
 - @now/node@0.12.1
 - @now/static-build@0.9.2
2019-07-30 07:38:20 -04:00
Steven
db88ad4b32 [master] Clean up package.json to match canary 2019-07-30 07:28:41 -04:00
Andy Bitz
4b846c3c88 Revert "[now-static-build] Allow to set the tag for zero-config builders"
This reverts commit d74d5141e4cb2c4e1e8a50bd66cad8d36f57e734.
2019-07-30 07:24:49 -04:00
Andy Bitz
2731435e3b [now-static-build] Allow to set the tag for zero-config builders 2019-07-30 07:24:43 -04:00
Steven
f18e1a6bd4 [now-static-build] Use ncc before publishing to npm (#839) 2019-07-30 07:24:31 -04:00
Steven
167d7bedec [now-next] Use ncc before publishing to npm (#832)
* [now-next] Change bridge build step

* Remove dependency on @now/node-bridge

* Test: print deploymentUrl

* Rename build script

* Use ncc before publishing

* Move deps to devDependencies
2019-07-30 07:24:11 -04:00
Sophearak Tha
cd0b1d61d1 [now-build-utils] Add debug() function (#831)
* Add `debug()` function

* Apply suggestions from code review

Co-Authored-By: Steven <steven@ceriously.com>

* Update packages/now-build-utils/src/debug.ts

Co-Authored-By: Steven <steven@ceriously.com>

* Apply suggestions from code review

Co-Authored-By: Steven <steven@ceriously.com>
2019-07-30 07:21:44 -04:00
Steven
86dc3708d2 [now-node] Fix 18-nested-tsconfig tests (#830)
* [now-node] Fix nested-tsconfig tests

* Remove unnecessary test
2019-07-30 07:21:19 -04:00
paulogdm
1e79eae029 Update _error.js route to return 404 status (#828) 2019-07-30 07:21:07 -04:00
paulogdm
d0c954210a Update types.ts (#829) 2019-07-30 07:20:55 -04:00
Steven
67c3481779 [now-build-utils] Use ncc before publishing to npm (#820)
* [now-build-utils] Use ncc before publish to npm

* Test all foreign fixtures

* Revert tests, but keep now-next
2019-07-30 07:20:42 -04:00
Steven
67473afd5b [now-node] Bump node-file-trace to 0.2.3 (#819)
* Silence vscode error

* Bump node-file-trace to 0.2.3

* Add test for mixed modules
2019-07-30 07:20:19 -04:00
Steven
3d52610acf [now-node] Fix symlink regression (#813) 2019-07-30 07:20:07 -04:00
Steven
729ba010c4 [now-node] Change TS default to esModuleInterop=true (#789)
* [now-node] Default to esModuleInterop=true

* Add test fixture 19-default-import-ts

* Change back to match ts-node
2019-07-30 07:19:51 -04:00
Steven
a470e563dc [now-node] Add logs with time for each step (#786)
* [now-node] Add logs with time for each step

* Fix typo
2019-07-30 07:19:33 -04:00
ywg-jean
15f674b8b7 [now-node] Yarn workspaces/transpilation followup (#782)
* tests transpilation of yarn workspaces

up until @now/node@0.7.4-canary.28 this used to work out of the box

* tests that typescript workspaces are properly compiled

* update node-file-trace, symlink handling

* update node-file-trace, use index.ts in package.json main

* remove logging!

* adds test with ts files in a subdir

* Updates typescript example to avoid node internal special casing

The initial example ended up passing even with a flawed implementation
because it unknowingly relied on a special case within node module's
resulotion engine which automaticaly resolves index files
within a module if they are located in the same directory as the
module's `package.json`

* upgrade to node-file-trace 0.2.2

* cleanup manual debugging leftovers

* fix yarn lock for node-file-trace 0.2.2

* more cleanup of manual tests

I offer my apologies about this mess
2019-07-30 07:19:15 -04:00
Andy Bitz
1e4e6b68e0 Publish
- @now/build-utils@0.9.4
2019-07-24 22:27:49 +02:00
Andy
2332100cff [now-build-utils] Do not use canary for zero config (#821) 2019-07-24 22:27:34 +02:00
Steven
9cb6f500b0 Publish
- @now/build-utils@0.9.3
 - @now/next@0.5.6
2019-07-23 14:45:11 -04:00
Joe Haddad
79a2bfde35 [now-next] Add immutable header to Next.js static assets (#810)
* Add immutable header to Next.js static assets

* Verify builder injects immutable header

* Use incorrect type

* Add missing field

* Add missing else clause
2019-07-23 14:43:29 -04:00
Luis Fernando Alvarez D
86a659e5c5 Normalize slash for nested paths (#788)
* Fixed slash issue in nested folders for Windows

* Added a test for dynamic routes in dev
2019-07-23 14:43:18 -04:00
Andy Bitz
0f00110db7 Publish
- @now/static-build@0.9.1
2019-07-22 20:51:23 +02:00
Andy
03ca6975ed [now-static-build] Use dev command in zero config when there is no (#808)
command
2019-07-22 20:50:25 +02:00
Andy
001813c529 [now-static-build] Adjust error message (#807) 2019-07-22 20:50:16 +02:00
Leo Lamprecht
570ef4824b Publish
- @now/static-build@0.9.0
2019-07-22 18:12:18 +00:00
Leo Lamprecht
ae9a43a0bd [now-static-build] Optimized Docusarus 2.0 (#809)
* Optimized Docusarus 2.0

* Better name for framework

* Added Docusaurus 1.0 test
2019-07-22 18:11:40 +00:00
Andy Bitz
ffaa5eaa17 Publish
- @now/build-utils@0.9.2
2019-07-21 20:31:50 +02:00
Andy
7f41a23a43 [now-build-utils] Improve builder detection (#803)
* [now-build-utils] Improve builder detection

* Changed message

* Change type

* Allow standalone public

* Removed unused interface

* Adjust test

* Adjust test
2019-07-21 20:31:21 +02:00
Leo Lamprecht
e80a1a5340 Publish
- @now/static-build@0.8.0
2019-07-20 13:46:54 +00:00
Leo Lamprecht
7eadaf889a Added zero config support for Ember (#801)
* Added zero config support for Ember

* Renamed dir

* Renamed dir

* Removed useless tests

* Trigger production build

* Fixed ember example
2019-07-20 13:38:07 +00:00
Andy Bitz
0823cc1005 Publish
- @now/static-build@0.7.5
2019-07-19 22:26:01 +02:00
Andy
11be2bf349 [now-static-build] Improve error message for the output directory for zero config (#795)
* [now-static-build] Improve error message for the output directory for zero config

* Add link
2019-07-19 22:24:58 +02:00
Andy Bitz
e8f31aebeb Publish
- @now/build-utils@0.9.1
2019-07-19 15:55:07 +02:00
Andy
162d27a38f [now-build-utils] Display link for missing build script warning (#792)
* [now-build-utils] Display link for missing `build` script warning

* Add link
2019-07-19 15:54:52 +02:00
Andy
355c007dbb [now-build-utils] Add nuxt builder detection (#798)
* [now-build-utils] Add nuxt builder detection

* Add tests

* Fix version
2019-07-19 15:54:47 +02:00
Andy Bitz
f946463cab Publish
- @now/static-build@0.7.4
2019-07-19 12:44:25 +02:00
Andy
a1e768ecc1 [now-static-build] Replace dist with public as output directory (#793)
* [now-static-build] Replace `dist` with `public` as output directory

* Fix test

* Revert "Fix test"

This reverts commit 351c84c7bea1313742c165f753e7481fc7c3c0fa.

* Revert "[now-static-build] Replace `dist` with `public` as output directory"

This reverts commit daedfac17729645d9b409d7df3da6b0fb5d3b56a.

* Apply only to zero config

* Fix path
2019-07-19 12:43:51 +02:00
Andy Bitz
539f9135dd Publish
- @now/build-utils@0.9.0
2019-07-19 00:35:45 +02:00
Andy
abd3d84d4c [now-build-utils] Improve static builds detection (#787)
* [now-build-utils] Improve static builds detection

* Don't deconstruct

* Allow null for package.json
2019-07-19 00:29:47 +02:00
Steven
0274893d31 Publish
- @now/bash@1.0.3
2019-07-18 10:32:09 -04:00
Steven
0ec767e0d0 Enhance readme publish steps (#783) 2019-07-18 10:19:34 -04:00
Steven
e6296fa06b Ignore yarn.lock changes on publish (#784) 2019-07-18 10:19:26 -04:00
Igor Klopov
35def42263 [now-bash] Respect meta.isDev flag (#778)
* switch from SRC to DIST, pass 'meta' to 'download'

* mkdir true directory of entrypoint

* lint fixes

* added a test

* Delete yarn-error.log
2019-07-18 10:19:18 -04:00
202 changed files with 26558 additions and 526 deletions

View File

@@ -28,4 +28,7 @@ else
echo "Publishing stable release"
fi
# Sometimes this is a false alarm and blocks publish
git checkout yarn.lock
yarn run lerna publish from-git $npm_tag --yes

View File

@@ -15,3 +15,4 @@
/packages/now-ruby/dist/*
/packages/now-static-build/dist/*
/packages/now-static-build/test/fixtures/**
/packages/now-routing-utils/dist/*

3
.github/CODEOWNERS vendored
View File

@@ -2,6 +2,7 @@
# https://help.github.com/en/articles/about-code-owners
* @styfle
/packages/now-build-utils @styfle @AndyBitz
/packages/now-node @styfle @tootallnate @lucleray
/packages/now-node-bridge @styfle @tootallnate @lucleray
/packages/now-next @timer
@@ -9,3 +10,5 @@
/packages/now-python @styfle @sophearak
/packages/now-rust @styfle @mike-engel @anmonteiro
/packages/now-ruby @styfle @coetry @nathancahill
/packages/now-static-build @styfle @AndyBitz
/packages/now-routing-utils @dav-is

View File

@@ -6,10 +6,10 @@ This is a monorepo containing the [Official Builders](https://zeit.co/docs/v2/de
There are two Channels:
| Channel | Git Branch | npm dist-tag | use example |
| ------- | ---------- | ------------ | ------------------ |
| Canary | `canary` | `@canary` | `@now/node@canary` |
| Stable | `master` | `@latest` | `@now/node@latest` |
| Channel | Git Branch | npm dist-tag | use example |
| ------- | ------------------------------------------------------------- | ------------ | ------------------ |
| Canary | [canary](https://github.com/zeit/now-builders/commits/canary) | `@canary` | `@now/node@canary` |
| Stable | [master](https://github.com/zeit/now-builders/commits/master) | `@latest` | `@now/node@latest` |
All PRs should be submitted to the `canary` branch.
@@ -30,14 +30,23 @@ For the Stable Channel, you must do the following:
- Deploy the modified Builders
```
git checkout master
git pull # make sure you're up to date
# View differences excluding "Publish" commits
git checkout canary && git pull
git log --pretty=format:"$ad- %s [%an]" | grep -v Publish > ~/Desktop/canary.txt
git checkout master && git pull
git log --pretty=format:"$ad- %s [%an]" | grep -v Publish > ~/Desktop/master.txt
diff ~/Desktop/canary.txt ~/Desktop/master.txt
# Cherry pick all PRs from canary into master ...
git cherry-pick <PR501_COMMIT_SHA>
git cherry-pick <PR502_COMMIT_SHA>
git cherry-pick <PR503_COMMIT_SHA>
git cherry-pick <PR504_COMMIT_SHA>
# ... etc ...
# Verify the only difference is "version" in package.json
git diff origin/canary
# Ship it
yarn publish-stable
```

View File

@@ -44,6 +44,7 @@
},
"devDependencies": {
"@types/node": "*",
"@zeit/ncc": "0.20.4",
"async-retry": "1.2.3",
"buffer-replace": "1.0.0",
"codecov": "3.2.0",

View File

@@ -11,12 +11,12 @@ curl -sfLS https://import.pw > "$IMPORT_BIN"
chmod +x "$IMPORT_BIN"
# For now only the entrypoint file is copied into the lambda
mkdir -p "$(dirname "$ENTRYPOINT")"
cp "$SRC/$ENTRYPOINT" "$ENTRYPOINT"
mkdir -p "$(dirname "$DIST/$ENTRYPOINT")"
cp "$ENTRYPOINT" "$DIST/$ENTRYPOINT"
# Copy in the runtime
cp "$BUILDER/runtime.sh" "$IMPORT_CACHE"
cp "$BUILDER/bootstrap" .
cp "$BUILDER/bootstrap" "$DIST"
# Load `import`
. "$(which import)"
@@ -24,7 +24,7 @@ cp "$BUILDER/bootstrap" .
# Cache runtime and user dependencies
echo "Caching imports in \"$ENTRYPOINT\"…"
. "$IMPORT_CACHE/runtime.sh"
. "$ENTRYPOINT"
. "$DIST/$ENTRYPOINT"
echo "Done caching imports"
# Run user build script

View File

@@ -5,7 +5,6 @@ const {
glob,
download,
createLambda,
getWriteableDirectory,
shouldServe,
} = require('@now/build-utils'); // eslint-disable-line import/no-extraneous-dependencies
@@ -21,12 +20,12 @@ const allowedConfigImports = new Set([
exports.analyze = ({ files, entrypoint }) => files[entrypoint].digest;
exports.build = async ({
workPath, files, entrypoint, config,
workPath, files, entrypoint, meta, config,
}) => {
const srcDir = await getWriteableDirectory();
console.log('downloading files...');
await download(files, srcDir);
await download(files, workPath, meta);
const distPath = join(workPath, 'dist');
const configEnv = Object.keys(config).reduce((o, v) => {
const name = snakeCase(v).toUpperCase();
@@ -46,11 +45,11 @@ exports.build = async ({
});
}
const IMPORT_CACHE = `${workPath}/.import-cache`;
const IMPORT_CACHE = `${distPath}/.import-cache`;
const env = Object.assign({}, process.env, configEnv, {
PATH: `${IMPORT_CACHE}/bin:${process.env.PATH}`,
IMPORT_CACHE,
SRC: srcDir,
DIST: distPath,
BUILDER: __dirname,
ENTRYPOINT: entrypoint,
});
@@ -64,7 +63,7 @@ exports.build = async ({
});
const lambda = await createLambda({
files: await glob('**', workPath),
files: await glob('**', distPath),
handler: entrypoint, // not actually used in `bootstrap`
runtime: 'provided',
environment: Object.assign({}, configEnv, {

View File

@@ -1,6 +1,6 @@
{
"name": "@now/bash",
"version": "1.0.2",
"version": "1.0.3",
"description": "Now 2.0 builder for HTTP endpoints written in Bash",
"main": "index.js",
"author": "Nathan Rajlich <nate@zeit.co>",
@@ -21,5 +21,8 @@
"dependencies": {
"execa": "^1.0.0",
"snake-case": "^2.1.0"
},
"scripts": {
"test": "jest"
}
}

View File

@@ -0,0 +1,3 @@
handler() {
echo "cow:RANDOMNESS_PLACEHOLDER"
}

View File

@@ -0,0 +1,11 @@
{
"version": 2,
"builds": [
{ "src": "index.sh", "use": "@now/bash" },
{ "src": "subdirectory/index.sh", "use": "@now/bash" }
],
"probes": [
{ "path": "/", "mustContain": "cow:RANDOMNESS_PLACEHOLDER" },
{ "path": "/subdirectory/", "mustContain": "yoda:RANDOMNESS_PLACEHOLDER" }
]
}

View File

@@ -0,0 +1,3 @@
handler() {
echo "yoda:RANDOMNESS_PLACEHOLDER"
}

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

@@ -1,146 +0,0 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
cross-spawn@^6.0.0:
version "6.0.5"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==
dependencies:
nice-try "^1.0.4"
path-key "^2.0.1"
semver "^5.5.0"
shebang-command "^1.2.0"
which "^1.2.9"
end-of-stream@^1.1.0:
version "1.4.1"
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43"
integrity sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==
dependencies:
once "^1.4.0"
execa@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8"
integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==
dependencies:
cross-spawn "^6.0.0"
get-stream "^4.0.0"
is-stream "^1.1.0"
npm-run-path "^2.0.0"
p-finally "^1.0.0"
signal-exit "^3.0.0"
strip-eof "^1.0.0"
get-stream@^4.0.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5"
integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==
dependencies:
pump "^3.0.0"
is-stream@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ=
isexe@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
lower-case@^1.1.1:
version "1.1.4"
resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac"
integrity sha1-miyr0bno4K6ZOkv31YdcOcQujqw=
nice-try@^1.0.4:
version "1.0.5"
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
no-case@^2.2.0:
version "2.3.2"
resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac"
integrity sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==
dependencies:
lower-case "^1.1.1"
npm-run-path@^2.0.0:
version "2.0.2"
resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f"
integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=
dependencies:
path-key "^2.0.0"
once@^1.3.1, once@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
dependencies:
wrappy "1"
p-finally@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae"
integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=
path-key@^2.0.0, path-key@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=
pump@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64"
integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==
dependencies:
end-of-stream "^1.1.0"
once "^1.3.1"
semver@^5.5.0:
version "5.6.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004"
integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==
shebang-command@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"
integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=
dependencies:
shebang-regex "^1.0.0"
shebang-regex@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3"
integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=
signal-exit@^3.0.0:
version "3.0.2"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=
snake-case@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-2.1.0.tgz#41bdb1b73f30ec66a04d4e2cad1b76387d4d6d9f"
integrity sha1-Qb2xtz8w7GagTU4srRt2OH1NbZ8=
dependencies:
no-case "^2.2.0"
strip-eof@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf"
integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=
which@^1.2.9:
version "1.3.1"
resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
dependencies:
isexe "^2.0.0"
wrappy@1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=

View File

@@ -0,0 +1,6 @@
tsc
rm dist/index.js
ncc build src/index.ts -o dist/main
mv dist/main/index.js dist/index.js
rm -rf dist/main

View File

@@ -1 +1 @@
module.exports = require('../dist/fs/download').default;
module.exports = require('../dist/index').download;

View File

@@ -1 +1 @@
module.exports = require('../dist/fs/get-writable-directory').default;
module.exports = require('../dist/index').getWriteableDirectory;

View File

@@ -1 +1 @@
module.exports = require('../dist/fs/glob').default;
module.exports = require('../dist/index').glob;

View File

@@ -1 +1 @@
module.exports = require('../dist/fs/rename').default;
module.exports = require('../dist/index').rename;

View File

@@ -1 +1 @@
module.exports = require('../dist/fs/run-user-scripts');
module.exports = require('../dist/index');

View File

@@ -1 +1 @@
module.exports = require('../dist/fs/stream-to-buffer').default;
module.exports = require('../dist/index').streamToBuffer;

View File

@@ -1,6 +1,6 @@
{
"name": "@now/build-utils",
"version": "0.8.9",
"version": "0.9.6",
"license": "MIT",
"main": "./dist/index.js",
"types": "./dist/index.d.js",
@@ -11,23 +11,9 @@
"directory": "packages/now-build-utils"
},
"scripts": {
"build": "tsc",
"test": "tsc && jest",
"prepublishOnly": "tsc"
},
"dependencies": {
"async-retry": "1.2.3",
"async-sema": "2.1.4",
"cross-spawn": "6.0.5",
"end-of-stream": "1.4.1",
"fs-extra": "7.0.0",
"glob": "7.1.3",
"into-stream": "5.0.0",
"minimatch": "3.0.4",
"multistream": "2.1.1",
"node-fetch": "2.2.0",
"semver": "6.1.1",
"yazl": "2.4.3"
"build": "./build.sh",
"test": "./build.sh && jest",
"prepublishOnly": "./build.sh"
},
"devDependencies": {
"@types/async-retry": "^1.2.1",
@@ -39,7 +25,19 @@
"@types/node-fetch": "^2.1.6",
"@types/semver": "6.0.0",
"@types/yazl": "^2.4.1",
"async-retry": "1.2.3",
"async-sema": "2.1.4",
"cross-spawn": "6.0.5",
"end-of-stream": "1.4.1",
"execa": "^1.0.0",
"typescript": "3.5.2"
"fs-extra": "7.0.0",
"glob": "7.1.3",
"into-stream": "5.0.0",
"minimatch": "3.0.4",
"multistream": "2.1.1",
"node-fetch": "2.2.0",
"semver": "6.1.1",
"typescript": "3.5.2",
"yazl": "2.4.3"
}
}

View File

@@ -0,0 +1,7 @@
export default function debug(message: string, ...additional: any[]) {
if (process.env.NOW_BUILDER_DEBUG) {
console.log(message, ...additional);
} else if (process.env.NOW_BUILDER_ANNOTATE) {
console.log(`[now-builder-debug] ${message}`, ...additional);
}
}

View File

@@ -1,110 +0,0 @@
import { PackageJson, Builder, Config } from './types';
import minimatch from 'minimatch';
const src: string = 'package.json';
const config: Config = { zeroConfig: true };
// Static builders are special cased in `@now/static-build`
const BUILDERS = new Map<string, Builder>([
['next', { src, use: '@now/next', config }],
]);
const API_BUILDERS: Builder[] = [
{ src: 'api/**/*.js', use: '@now/node@canary', config },
{ src: 'api/**/*.ts', use: '@now/node@canary', config },
{ src: 'api/**/*.rs', use: '@now/rust', config },
{ src: 'api/**/*.go', use: '@now/go', config },
{ src: 'api/**/*.php', use: '@now/php', config },
{ src: 'api/**/*.py', use: '@now/python', config },
{ src: 'api/**/*.rb', use: '@now/ruby', config },
{ src: 'api/**/*.sh', use: '@now/bash', config },
];
interface Warning {
code: string;
message: string;
}
export async function detectBuilder(
pkg: PackageJson
): Promise<{
builder: null | Builder;
warnings: null | Warning[];
}> {
let warnings: null | Warning[] = null;
const scripts = pkg.scripts || {};
if (!scripts.build) {
warnings = [
{
code: 'missing_build_script',
message:
'Your `package.json` file is missing a `build` property inside the `script` property',
},
];
}
for (const [dependency, builder] of BUILDERS) {
const deps = Object.assign({}, pkg.dependencies, pkg.devDependencies);
// Return the builder when a dependency matches
if (deps[dependency]) {
return { builder, warnings };
}
}
// If there is no `build` and `now-build` script
// we'll not select `@now/static-build`
// since it would fail
if (!scripts.build) {
return { builder: null, warnings };
}
// By default we'll choose the `static-build` builder
const builder = { src, use: '@now/static-build', config };
return { builder, warnings };
}
// Files that match a specific pattern will get ignored
export function ignoreApiFilter(file: string) {
if (file.includes('/.')) {
return false;
}
if (file.includes('/_')) {
return false;
}
// If the file does not match any builder we also
// don't want to create a route e.g. `package.json`
if (API_BUILDERS.every(({ src }) => !minimatch(file, src))) {
return false;
}
return true;
}
// We need to sort the file paths by alphabet to make
// sure the routes stay in the same order e.g. for deduping
export function sortFiles(fileA: string, fileB: string) {
return fileA.localeCompare(fileB);
}
export async function detectApiBuilders(
files: string[]
): Promise<Builder[] | null> {
const builds = files
.sort(sortFiles)
.filter(ignoreApiFilter)
.map(file => {
const result = API_BUILDERS.find(
({ src }): boolean => minimatch(file, src)
);
return result ? { ...result, src: file } : null;
});
const finishedBuilds = builds.filter(Boolean);
return finishedBuilds.length > 0 ? (finishedBuilds as Builder[]) : null;
}

View File

@@ -0,0 +1,169 @@
import { PackageJson, Builder, Config } from './types';
import minimatch from 'minimatch';
interface ErrorResponse {
code: string;
message: string;
}
interface Options {
tag?: 'canary' | 'latest';
}
const src: string = 'package.json';
const config: Config = { zeroConfig: true };
// Static builders are special cased in `@now/static-build`
const BUILDERS = new Map<string, Builder>([
['next', { src, use: '@now/next', config }],
]);
const API_BUILDERS: Builder[] = [
{ src: 'api/**/*.js', use: '@now/node', config },
{ src: 'api/**/*.ts', use: '@now/node', config },
{ src: 'api/**/*.go', use: '@now/go', config },
{ src: 'api/**/*.py', use: '@now/python', config },
{ src: 'api/**/*.rb', use: '@now/ruby', config },
];
const MISSING_BUILD_SCRIPT_ERROR: ErrorResponse = {
code: 'missing_build_script',
message:
'Your `package.json` file is missing a `build` property inside the `script` property.' +
'\nMore details: https://zeit.co/docs/v2/advanced/platform/frequently-asked-questions#missing-build-script',
};
function hasPublicDirectory(files: string[]) {
return files.some(name => name.startsWith('public/'));
}
function hasBuildScript(pkg: PackageJson | undefined) {
const { scripts = {} } = pkg || {};
return Boolean(scripts && scripts['build']);
}
async function detectBuilder(pkg: PackageJson): Promise<Builder> {
for (const [dependency, builder] of BUILDERS) {
const deps = Object.assign({}, pkg.dependencies, pkg.devDependencies);
// Return the builder when a dependency matches
if (deps[dependency]) {
return builder;
}
}
// By default we'll choose the `static-build` builder
return { src, use: '@now/static-build', config };
}
// Files that match a specific pattern will get ignored
export function ignoreApiFilter(file: string) {
if (file.includes('/.')) {
return false;
}
if (file.includes('/_')) {
return false;
}
// If the file does not match any builder we also
// don't want to create a route e.g. `package.json`
if (API_BUILDERS.every(({ src }) => !minimatch(file, src))) {
return false;
}
return true;
}
// We need to sort the file paths by alphabet to make
// sure the routes stay in the same order e.g. for deduping
export function sortFiles(fileA: string, fileB: string) {
return fileA.localeCompare(fileB);
}
async function detectApiBuilders(files: string[]): Promise<Builder[]> {
const builds = files
.sort(sortFiles)
.filter(ignoreApiFilter)
.map(file => {
const result = API_BUILDERS.find(
({ src }): boolean => minimatch(file, src)
);
return result ? { ...result, src: file } : null;
});
const finishedBuilds = builds.filter(Boolean);
return finishedBuilds as Builder[];
}
// When zero config is used we can call this function
// to determine what builders to use
export async function detectBuilders(
files: string[],
pkg?: PackageJson | undefined | null,
options?: Options
): Promise<{
builders: Builder[] | null;
errors: ErrorResponse[] | null;
}> {
const errors: ErrorResponse[] = [];
// Detect all builders for the `api` directory before anything else
let builders = await detectApiBuilders(files);
if (pkg && hasBuildScript(pkg)) {
builders.push(await detectBuilder(pkg));
} else {
if (pkg && builders.length === 0) {
// We only show this error when there are no api builders
// since the dependencies of the pkg could be used for those
errors.push(MISSING_BUILD_SCRIPT_ERROR);
return { errors, builders: null };
}
// We allow a `public` directory
// when there are no build steps
if (hasPublicDirectory(files)) {
builders.push({
use: '@now/static',
src: 'public/**/*',
config,
});
} else if (builders.length > 0) {
// We can't use pattern matching, since `!(api)` and `!(api)/**/*`
// won't give the correct results
builders.push(
...files
.filter(name => !name.startsWith('api/'))
.filter(name => !(name === 'package.json'))
.map(name => ({
use: '@now/static',
src: name,
config,
}))
);
}
}
// Change the tag for the builders
if (builders && builders.length) {
const tag = options && options.tag;
if (tag) {
builders = builders.map((builder: Builder) => {
// @now/static has no canary builder
if (builder.use !== '@now/static') {
builder.use = `${builder.use}@${tag}`;
}
return builder;
});
}
}
return {
builders: builders.length ? builders : null,
errors: errors.length ? errors : null,
};
}

View File

@@ -1,6 +1,6 @@
import { Route } from './types';
import { Route, Builder } from './types';
import { parse as parsePath } from 'path';
import { ignoreApiFilter, sortFiles } from './detect-builder';
import { ignoreApiFilter, sortFiles } from './detect-builders';
function joinPath(...segments: string[]) {
const joinedPath = segments.join('/');
@@ -167,12 +167,12 @@ function sortFilesBySegmentCount(fileA: string, fileB: string): number {
return 0;
}
export async function detectApiRoutes(
files: string[]
): Promise<{
interface RoutesResult {
defaultRoutes: Route[] | null;
error: { [key: string]: string } | null;
}> {
}
async function detectApiRoutes(files: string[]): Promise<RoutesResult> {
if (!files || files.length === 0) {
return { defaultRoutes: null, error: null };
}
@@ -234,3 +234,29 @@ export async function detectApiRoutes(
return { defaultRoutes, error: null };
}
function hasPublicBuilder(builders: Builder[]): boolean {
return builders.some(
builder =>
builder.use === '@now/static' &&
builder.src === 'public/**/*' &&
builder.config &&
builder.config.zeroConfig === true
);
}
export async function detectRoutes(
files: string[],
builders: Builder[]
): Promise<RoutesResult> {
const routesResult = await detectApiRoutes(files);
if (routesResult.defaultRoutes && hasPublicBuilder(builders)) {
routesResult.defaultRoutes.push({
src: '/(.*)',
dest: '/public/$1',
});
}
return routesResult;
}

View File

@@ -16,8 +16,9 @@ import {
} from './fs/run-user-scripts';
import streamToBuffer from './fs/stream-to-buffer';
import shouldServe from './should-serve';
import { detectBuilder, detectApiBuilders } from './detect-builder';
import { detectApiRoutes } from './detect-routes';
import { detectBuilders } from './detect-builders';
import { detectRoutes } from './detect-routes';
import debug from './debug';
export {
FileBlob,
@@ -38,9 +39,9 @@ export {
getSpawnOptions,
streamToBuffer,
shouldServe,
detectBuilder,
detectApiBuilders,
detectApiRoutes,
detectBuilders,
detectRoutes,
debug,
};
export * from './types';

View File

@@ -23,6 +23,8 @@ export interface Route {
headers?: {
[key: string]: string;
};
continue?: boolean;
status?: number;
}
export interface Config {

View File

@@ -16,11 +16,7 @@ const {
testDeployment,
} = require('../../../test/lib/deployment/test-deployment.js');
const {
detectBuilder,
detectApiBuilders,
detectApiRoutes,
} = require('../dist');
const { detectBuilders, detectRoutes } = require('../dist');
jest.setTimeout(4 * 60 * 1000);
const builderUrl = '@canary';
@@ -120,6 +116,36 @@ it('should match all semver ranges', () => {
);
});
it('should support require by path for legacy builders', () => {
const index = require('@now/build-utils');
const download2 = require('@now/build-utils/fs/download.js');
const getWriteableDirectory2 = require('@now/build-utils/fs/get-writable-directory.js');
const glob2 = require('@now/build-utils/fs/glob.js');
const rename2 = require('@now/build-utils/fs/rename.js');
const {
runNpmInstall: runNpmInstall2,
} = require('@now/build-utils/fs/run-user-scripts.js');
const streamToBuffer2 = require('@now/build-utils/fs/stream-to-buffer.js');
const FileBlob2 = require('@now/build-utils/file-blob.js');
const FileFsRef2 = require('@now/build-utils/file-fs-ref.js');
const FileRef2 = require('@now/build-utils/file-ref.js');
const { Lambda: Lambda2 } = require('@now/build-utils/lambda.js');
expect(download2).toBe(index.download);
expect(getWriteableDirectory2).toBe(index.getWriteableDirectory);
expect(glob2).toBe(index.glob);
expect(rename2).toBe(index.rename);
expect(runNpmInstall2).toBe(index.runNpmInstall);
expect(streamToBuffer2).toBe(index.streamToBuffer);
expect(FileBlob2).toBe(index.FileBlob);
expect(FileFsRef2).toBe(index.FileFsRef);
expect(FileRef2).toBe(index.FileRef);
expect(Lambda2).toBe(index.Lambda);
});
// own fixtures
const fixturesPath = path.resolve(__dirname, 'fixtures');
@@ -139,7 +165,7 @@ for (const fixture of fs.readdirSync(fixturesPath)) {
// few foreign tests
const buildersToTestWith = ['now-node', 'now-static-build'];
const buildersToTestWith = ['now-next', 'now-node', 'now-static-build'];
// eslint-disable-next-line no-restricted-syntax
for (const builder of buildersToTestWith) {
@@ -151,7 +177,7 @@ for (const builder of buildersToTestWith) {
// eslint-disable-next-line no-restricted-syntax
for (const fixture of fs.readdirSync(fixturesPath2)) {
// don't run all foreign fixtures, just some
if (['01-cowsay', '03-env-vars'].includes(fixture)) {
if (['01-cowsay', '01-cache-headers', '03-env-vars'].includes(fixture)) {
// eslint-disable-next-line no-loop-func
it(`should build ${builder}/${fixture}`, async () => {
await expect(
@@ -165,68 +191,269 @@ for (const builder of buildersToTestWith) {
}
}
it('Test `detectBuilder`', async () => {
it('Test `detectBuilders`', async () => {
{
const pkg = { dependencies: { next: '1.0.0' } };
const { builder, warnings } = await detectBuilder(pkg);
expect(builder.use).toBe('@now/next');
expect(warnings.length).toBe(1);
}
{
const pkg = { devDependencies: { next: '1.0.0' } };
const { builder } = await detectBuilder(pkg);
expect(builder.use).toBe('@now/next');
}
{
const pkg = {};
const { builder } = await detectBuilder(pkg);
expect(builder).toBe(null);
}
});
it('Test `detectApiBuilders`', async () => {
{
const files = ['package.json', 'api/user.js', 'api/team.js'];
const builders = await detectApiBuilders(files);
expect(builders[0].use).toBe('@now/node@canary');
}
{
const files = ['package.json', 'api/user.go', 'api/team.js'];
const builders = await detectApiBuilders(files);
expect(builders.some(({ use }) => use === '@now/go')).toBeTruthy();
expect(builders.some(({ use }) => use === '@now/node@canary')).toBeTruthy();
}
{
const files = ['package.json'];
const builders = await detectApiBuilders(files);
// package.json + no build
const pkg = { dependencies: { next: '9.0.0' } };
const files = ['package.json', 'pages/index.js', 'public/index.html'];
const { builders, errors } = await detectBuilders(files, pkg);
expect(builders).toBe(null);
expect(errors.length).toBe(1);
}
{
// package.json + no build + next
const pkg = {
scripts: { build: 'next build' },
dependencies: { next: '9.0.0' },
};
const files = ['package.json', 'pages/index.js'];
const { builders, errors } = await detectBuilders(files, pkg);
expect(builders[0].use).toBe('@now/next');
expect(errors).toBe(null);
}
{
// package.json + no build + next
const pkg = {
scripts: { build: 'next build' },
devDependencies: { next: '9.0.0' },
};
const files = ['package.json', 'pages/index.js'];
const { builders, errors } = await detectBuilders(files, pkg);
expect(builders[0].use).toBe('@now/next');
expect(errors).toBe(null);
}
{
// package.json + no build
const pkg = {};
const files = ['package.json'];
const { builders, errors } = await detectBuilders(files, pkg);
expect(builders).toBe(null);
expect(errors.length).toBe(1);
}
{
// static file
const files = ['index.html'];
const { builders, errors } = await detectBuilders(files);
expect(builders).toBe(null);
expect(errors).toBe(null);
}
{
// no package.json + public
const files = ['api/users.js', 'public/index.html'];
const { builders, errors } = await detectBuilders(files);
expect(builders[1].use).toBe('@now/static');
expect(errors).toBe(null);
}
{
// no package.json + no build + raw static + api
const files = ['api/users.js', 'index.html'];
const { builders, errors } = await detectBuilders(files);
expect(builders[0].use).toBe('@now/node');
expect(builders[0].src).toBe('api/users.js');
expect(builders[1].use).toBe('@now/static');
expect(builders[1].src).toBe('index.html');
expect(builders.length).toBe(2);
expect(errors).toBe(null);
}
{
// package.json + no build + root + api
const files = ['index.html', 'api/[endpoint].js', 'static/image.png'];
const { builders, errors } = await detectBuilders(files);
expect(builders[0].use).toBe('@now/node');
expect(builders[0].src).toBe('api/[endpoint].js');
expect(builders[1].use).toBe('@now/static');
expect(builders[1].src).toBe('index.html');
expect(builders[2].use).toBe('@now/static');
expect(builders[2].src).toBe('static/image.png');
expect(builders.length).toBe(3);
expect(errors).toBe(null);
}
{
// api + ignore files
const files = [
'api/users/[id].js',
'api/_utils/handler.js',
'api/users/.helper.js',
'api/teams/_helper.js',
'api/[endpoint]/.helper.js',
'api/[endpoint]/[id].js',
];
const builders = await detectApiBuilders(files);
const { builders } = await detectBuilders(files);
expect(builders[0].use).toBe('@now/node');
expect(builders[0].src).toBe('api/[endpoint]/[id].js');
expect(builders.length).toBe(1);
}
{
// api + next + public
const pkg = {
scripts: { build: 'next build' },
devDependencies: { next: '9.0.0' },
};
const files = ['package.json', 'api/endpoint.js', 'public/index.html'];
const { builders } = await detectBuilders(files, pkg);
expect(builders[0].use).toBe('@now/node');
expect(builders[0].src).toBe('api/endpoint.js');
expect(builders[1].use).toBe('@now/next');
expect(builders[1].src).toBe('package.json');
expect(builders.length).toBe(2);
}
{
// api + next + raw static
const pkg = {
scripts: { build: 'next build' },
devDependencies: { next: '9.0.0' },
};
const files = ['package.json', 'api/endpoint.js', 'index.html'];
const { builders } = await detectBuilders(files, pkg);
expect(builders[0].use).toBe('@now/node');
expect(builders[0].src).toBe('api/endpoint.js');
expect(builders[1].use).toBe('@now/next');
expect(builders[1].src).toBe('package.json');
expect(builders.length).toBe(2);
}
{
// api + raw static
const files = ['api/endpoint.js', 'index.html', 'favicon.ico'];
const { builders } = await detectBuilders(files);
expect(builders[0].use).toBe('@now/node');
expect(builders[0].src).toBe('api/endpoint.js');
expect(builders[1].use).toBe('@now/static');
expect(builders[1].src).toBe('favicon.ico');
expect(builders[2].use).toBe('@now/static');
expect(builders[2].src).toBe('index.html');
expect(builders.length).toBe(3);
}
{
// api + public
const files = [
'api/endpoint.js',
'public/index.html',
'public/favicon.ico',
'README.md',
];
const { builders } = await detectBuilders(files);
expect(builders[0].use).toBe('@now/node');
expect(builders[0].src).toBe('api/endpoint.js');
expect(builders[1].use).toBe('@now/static');
expect(builders[1].src).toBe('public/**/*');
expect(builders.length).toBe(2);
}
{
// just public
const files = ['public/index.html', 'public/favicon.ico', 'README.md'];
const { builders } = await detectBuilders(files);
expect(builders[0].src).toBe('public/**/*');
expect(builders.length).toBe(1);
}
{
// next + public
const pkg = {
scripts: { build: 'next build' },
devDependencies: { next: '9.0.0' },
};
const files = ['package.json', 'public/index.html', 'README.md'];
const { builders } = await detectBuilders(files, pkg);
expect(builders[0].use).toBe('@now/next');
expect(builders[0].src).toBe('package.json');
expect(builders.length).toBe(1);
}
{
// nuxt
const pkg = {
scripts: { build: 'nuxt build' },
dependencies: { nuxt: '2.8.1' },
};
const files = ['package.json', 'pages/index.js'];
const { builders } = await detectBuilders(files, pkg);
expect(builders[0].use).toBe('@now/static-build');
expect(builders[0].src).toBe('package.json');
expect(builders.length).toBe(1);
}
{
// package.json with no build + api
const pkg = { dependencies: { next: '9.0.0' } };
const files = ['package.json', 'api/[endpoint].js'];
const { builders } = await detectBuilders(files, pkg);
expect(builders[0].use).toBe('@now/node');
expect(builders[0].src).toBe('api/[endpoint].js');
expect(builders.length).toBe(1);
}
{
// package.json with no build + public directory
const pkg = { dependencies: { next: '9.0.0' } };
const files = ['package.json', 'public/index.html'];
const { builders, errors } = await detectBuilders(files, pkg);
expect(builders).toBe(null);
expect(errors.length).toBe(1);
}
{
// no package.json + api
const files = ['api/[endpoint].js', 'api/[endpoint]/[id].js'];
const { builders } = await detectBuilders(files);
expect(builders.length).toBe(2);
}
{
// no package.json + no api
const files = ['index.html'];
const { builders, errors } = await detectBuilders(files);
expect(builders).toBe(null);
expect(errors).toBe(null);
}
{
// package.json + api + canary
const pkg = {
scripts: { build: 'next build' },
dependencies: { next: '9.0.0' },
};
const files = [
'pages/index.js',
'api/[endpoint].js',
'api/[endpoint]/[id].js',
];
const { builders } = await detectBuilders(files, pkg, { tag: 'canary' });
expect(builders[0].use).toBe('@now/node@canary');
expect(builders[1].use).toBe('@now/node@canary');
expect(builders[2].use).toBe('@now/next@canary');
expect(builders.length).toBe(3);
}
});
it('Test `detectApiRoutes`', async () => {
it('Test `detectRoutes`', async () => {
{
const files = ['api/user.go', 'api/team.js', 'api/package.json'];
const { defaultRoutes } = await detectApiRoutes(files);
const { builders } = await detectBuilders(files);
const { defaultRoutes } = await detectRoutes(files, builders);
expect(defaultRoutes.length).toBe(2);
expect(defaultRoutes[0].dest).toBe('/api/team.js');
expect(defaultRoutes[1].dest).toBe('/api/user.go');
@@ -235,39 +462,58 @@ it('Test `detectApiRoutes`', async () => {
{
const files = ['api/user.go', 'api/user.js'];
const { error } = await detectApiRoutes(files);
const { builders } = await detectBuilders(files);
const { error } = await detectRoutes(files, builders);
expect(error.code).toBe('conflicting_file_path');
}
{
const files = ['api/[user].go', 'api/[team]/[id].js'];
const { error } = await detectApiRoutes(files);
const { builders } = await detectBuilders(files);
const { error } = await detectRoutes(files, builders);
expect(error.code).toBe('conflicting_file_path');
}
{
const files = ['api/[team]/[team].js'];
const { error } = await detectApiRoutes(files);
const { builders } = await detectBuilders(files);
const { error } = await detectRoutes(files, builders);
expect(error.code).toBe('conflicting_path_segment');
}
{
const files = ['api/[endpoint].js', 'api/[endpoint]/[id].js'];
const { defaultRoutes } = await detectApiRoutes(files);
const { builders } = await detectBuilders(files);
const { defaultRoutes } = await detectRoutes(files, builders);
expect(defaultRoutes.length).toBe(2);
}
{
const files = [
'api/_utils/handler.js',
'api/[endpoint]/.helper.js',
'public/index.html',
'api/[endpoint].js',
'api/[endpoint]/[id].js',
];
const builders = await detectApiBuilders(files);
expect(builders.length).toBe(1);
const { builders } = await detectBuilders(files);
const { defaultRoutes } = await detectRoutes(files, builders);
expect(defaultRoutes[2].src).toBe('/(.*)');
expect(defaultRoutes[2].dest).toBe('/public/$1');
expect(defaultRoutes.length).toBe(3);
}
{
const pkg = {
scripts: { build: 'next build' },
devDependencies: { next: '9.0.0' },
};
const files = ['public/index.html', 'api/[endpoint].js'];
const { builders } = await detectBuilders(files, pkg);
const { defaultRoutes } = await detectRoutes(files, builders);
expect(defaultRoutes.length).toBe(1);
}
});

View File

@@ -3,4 +3,3 @@ node_modules
/go
/analyze
*.js
!util/install.js

View File

@@ -0,0 +1,6 @@
import { downloadGo } from './go-helpers';
downloadGo().catch(err => {
console.error(err);
process.exit(1);
});

View File

@@ -1,6 +1,6 @@
{
"name": "@now/go",
"version": "0.5.6",
"version": "0.5.7",
"license": "MIT",
"homepage": "https://zeit.co/docs/v2/deployments/official-builders/go-now-go",
"repository": {
@@ -11,7 +11,8 @@
"scripts": {
"build": "tsc",
"test": "tsc && jest",
"prepublish": "tsc"
"prepublish": "tsc",
"now-postinstall": "node install.js"
},
"files": [
"*.js",

View File

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

16
packages/now-next/build.sh Executable file
View File

@@ -0,0 +1,16 @@
#!/bin/bash
set -euo pipefail
bridge_defs="$(dirname $(pwd))/now-node-bridge/src/bridge.ts"
cp -v "$bridge_defs" src/now__bridge.ts
tsc
ncc build src/dev-server.ts -o dist/dev
mv dist/dev/index.js dist/dev-server.js
rm -rf dist/dev
ncc build src/index.ts -o dist/main
mv dist/main/index.js dist/index.js
rm -rf dist/main

View File

@@ -1,11 +0,0 @@
#!/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,33 +1,30 @@
{
"name": "@now/next",
"version": "0.5.5",
"version": "0.5.8",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://zeit.co/docs/v2/deployments/official-builders/next-js-now-next",
"scripts": {
"build": "./getBridgeTypes.sh && tsc",
"test": "npm run build && jest",
"prepublish": "yarn run build"
"build": "./build.sh",
"test": "./build.sh && jest",
"prepublishOnly": "./build.sh"
},
"repository": {
"type": "git",
"url": "https://github.com/zeit/now-builders.git",
"directory": "packages/now-next"
},
"dependencies": {
"@now/node-bridge": "1.2.3",
"fs-extra": "^7.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",
"@types/next-server": "8.0.0",
"@types/resolve-from": "5.0.1",
"@types/semver": "6.0.0",
"fs-extra": "7.0.0",
"get-port": "5.0.0",
"resolve-from": "5.0.0",
"semver": "6.1.1",
"typescript": "3.5.2"
}
}

View File

@@ -1,4 +1,4 @@
import { ChildProcess, fork, SpawnOptions } from 'child_process';
import { ChildProcess, fork } from 'child_process';
import {
pathExists,
readFile,
@@ -16,33 +16,34 @@ import {
FileBlob,
FileFsRef,
Files,
getNodeVersion,
getSpawnOptions,
glob,
Lambda,
PrepareCacheOptions,
runNpmInstall,
runPackageJsonScript,
getNodeVersion,
getSpawnOptions,
Route,
} from '@now/build-utils';
import createServerlessConfig from './create-serverless-config';
import nextLegacyVersions from './legacy-versions';
import {
EnvConfig,
excludeFiles,
filesFromDirectory,
getDynamicRoutes,
getNextConfig,
getPathsInside,
getRoutes,
includeOnlyEntryDirectory,
isDynamicRoute,
normalizePackageJson,
filesFromDirectory,
normalizePage,
stringMap,
syncEnvVars,
validateEntrypoint,
normalizePage,
getDynamicRoutes,
isDynamicRoute,
} from './utils';
import createServerlessConfig from './create-serverless-config';
interface BuildParamsMeta {
isDev: boolean | undefined;
@@ -157,7 +158,7 @@ export const build = async ({
entrypoint,
meta = {} as BuildParamsMeta,
}: BuildParamsType): Promise<{
routes?: ({ src?: string; dest?: string } | { handle: string })[];
routes: Route[];
output: Files;
watch?: string[];
childProcesses: ChildProcess[];
@@ -301,7 +302,7 @@ export const build = async ({
await unlinkFile(path.join(entryPath, '.npmrc'));
}
const exportedPageRoutes: { src: string; dest: string }[] = [];
const exportedPageRoutes: Route[] = [];
const lambdas: { [key: string]: Lambda } = {};
const staticPages: { [key: string]: FileFsRef } = {};
const dynamicPages: string[] = [];
@@ -330,7 +331,7 @@ export const build = async ({
);
const launcherFiles = {
'now__bridge.js': new FileFsRef({
fsPath: require('@now/node-bridge'),
fsPath: path.join(__dirname, 'now__bridge.js'),
}),
};
const nextFiles: { [key: string]: FileFsRef } = {
@@ -394,7 +395,7 @@ export const build = async ({
console.log('preparing lambda files...');
const launcherFiles = {
'now__bridge.js': new FileFsRef({
fsPath: require('@now/node-bridge'),
fsPath: path.join(__dirname, 'now__bridge.js'),
}),
'now__launcher.js': new FileFsRef({
fsPath: path.join(__dirname, 'launcher.js'),
@@ -536,6 +537,16 @@ export const build = async ({
routes: [
// Static exported pages (.html rewrites)
...exportedPageRoutes,
// Before we handle static files we need to set proper caching headers
{
// This ensures we only match known emitted-by-Next.js files and not
// user-emitted files which may be missing a hash in their filename.
src: '/_next/static/(?:[^/]+/pages|chunks|runtime)/.+',
// Next.js assets contain a hash or entropy in their filenames, so they
// are guaranteed to be unique and cacheable indefinitely.
headers: { 'cache-control': 'public,max-age=31536000,immutable' },
continue: true,
},
// Next.js page lambdas, `static/` folder, reserved assets, and `public/`
// folder
{ handle: 'filesystem' },
@@ -547,6 +558,7 @@ export const build = async ({
{
src: path.join('/', entryDirectory, '.*'),
dest: path.join('/', entryDirectory, '_error'),
status: 404,
},
]),
],

View File

@@ -3,8 +3,8 @@ if (!process.env.NODE_ENV) {
process.env.NOW_REGION === 'dev1' ? 'development' : 'production';
}
const { Server } = require('http');
const { Bridge } = require('./now__bridge');
import { Server } from 'http';
import { Bridge } from './now__bridge';
const page = require('./page');
// page.render is for React rendering

View File

@@ -228,7 +228,7 @@ function getRoutes(
const relativeToPages = path.relative('pages', relativePath);
const extension = path.extname(relativeToPages);
const pageName = relativeToPages.replace(extension, '');
const pageName = relativeToPages.replace(extension, '').replace(/\\/g, '/');
if (pageName.startsWith('_')) {
continue;

View File

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

View File

@@ -0,0 +1,12 @@
{
"version": 2,
"builds": [{ "src": "package.json", "use": "@now/next" }],
"probes": [
{
"path": "/_next/static/testing-build-id/pages/index.js",
"responseHeaders": {
"cache-control": "public,max-age=31536000,immutable"
}
}
]
}

View File

@@ -0,0 +1,7 @@
{
"dependencies": {
"next": "latest",
"react": "^16.8.6",
"react-dom": "^16.8.6"
}
}

View File

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

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

@@ -22,6 +22,24 @@ describe('build meta dev', () => {
export default () => 'Index page'
`,
}),
'pages/nested/[param].js': new FileBlob({
mode: 0o777,
data: `
export default () => 'Dynamic page'
`,
}),
'pages/nested/page.tsx': new FileBlob({
mode: 0o777,
data: `
export default () => 'Nested page'
`,
}),
'pages/api/test.js': new FileBlob({
mode: 0o777,
data: `
export default (req, res) => res.status(200).end('API Route')
`,
}),
// This file should be omitted because `pages/index.js` will use the same route
'public/index': new FileBlob({
mode: 0o777,
@@ -39,9 +57,14 @@ describe('build meta dev', () => {
"now-build": "next build"
},
"dependencies": {
"next": "8",
"next": "9",
"react": "16",
"react-dom": "16"
},
"devDependencies": {
"@types/node": "12",
"@types/react": "16",
"typescript": "3"
}
}
`,
@@ -80,11 +103,20 @@ describe('build meta dev', () => {
{ src: '/static/(.*)', dest: 'http://localhost:5000/static/$1' },
{ src: '/index', dest: 'http://localhost:5000/index' },
{ src: '/', dest: 'http://localhost:5000/' },
{ src: '/nested/page', dest: 'http://localhost:5000/nested/page' },
{ src: '/api/test', dest: 'http://localhost:5000/api/test' },
{
src: '^/(nested\\/([^\\/]+?)(?:\\/)?)$',
dest: 'http://localhost:5000/$1',
},
{ src: '/data.txt', dest: 'http://localhost:5000/data.txt' },
]);
expect(watch).toEqual([
'next.config.js',
'pages/index.js',
'pages/nested/[param].js',
'pages/nested/page.tsx',
'pages/api/test.js',
'public/index',
'public/data.txt',
'package.json',

View File

@@ -0,0 +1,13 @@
describe('export', () => {
it('should require by path main', async () => {
const main = require('@now/next');
expect(main).toBeDefined();
});
it('should require by path dev-server relative to index', async () => {
const index = require('@now/next/dist/index.js');
const server = require('@now/next/dist/dev-server.js');
expect(index).toBeDefined();
expect(server).toBeDefined();
});
});

View File

@@ -6,7 +6,7 @@ const {
normalizePackageJson,
getNextConfig,
} = require('@now/next/dist/utils');
const FileRef = require('@now/build-utils/file-ref'); // eslint-disable-line import/no-extraneous-dependencies
const { FileRef } = require('@now/build-utils'); // eslint-disable-line import/no-extraneous-dependencies
describe('getNextConfig', () => {
const workPath = path.join(__dirname, 'fixtures');

View File

@@ -1,6 +1,6 @@
{
"name": "@now/node",
"version": "0.12.0",
"version": "0.12.3",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://zeit.co/docs/v2/deployments/official-builders/node-js-now-node",
@@ -28,7 +28,7 @@
"@types/etag": "1.8.0",
"@types/test-listen": "1.1.0",
"@zeit/ncc": "0.20.4",
"@zeit/node-file-trace": "0.2.1",
"@zeit/node-file-trace": "0.2.6",
"content-type": "1.0.4",
"cookie": "0.4.0",
"etag": "1.8.1",

View File

@@ -56,13 +56,17 @@ async function downloadInstallAndBundle({
meta,
}: DownloadOptions) {
console.log('downloading user files...');
const downloadTime = Date.now();
const downloadedFiles = await download(files, workPath, meta);
console.log(`download complete [${Date.now() - downloadTime}ms]`);
console.log("installing dependencies for user's code...");
const installTime = Date.now();
const entrypointFsDirname = join(workPath, dirname(entrypoint));
const nodeVersion = await getNodeVersion(entrypointFsDirname);
const spawnOpts = getSpawnOptions(meta, nodeVersion);
await runNpmInstall(entrypointFsDirname, ['--prefer-offline'], spawnOpts);
console.log(`install complete [${Date.now() - installTime}ms]`);
const entrypointPath = downloadedFiles[entrypoint].fsPath;
return { entrypointPath, entrypointFsDirname, nodeVersion, spawnOpts };
@@ -72,8 +76,7 @@ async function compile(
workPath: string,
entrypointPath: string,
entrypoint: string,
config: CompilerConfig,
{ isDev, filesChanged, filesRemoved }: Meta
config: CompilerConfig
): Promise<{
preparedFiles: Files;
shouldAddSourcemapSupport: boolean;
@@ -162,6 +165,7 @@ async function compile(
const { fileList, esmFileList } = await nodeFileTrace([...inputFiles], {
base: workPath,
ts: true,
mixedModules: true,
ignore: config.excludeFiles,
readFile(fsPath: string): Buffer | string | null {
const relPath = relative(workPath, fsPath);
@@ -175,9 +179,12 @@ async function compile(
source = compileTypeScript(fsPath, source.toString());
}
const { mode } = lstatSync(fsPath);
if (isSymbolicLink(mode))
throw new Error('Internal error: Unexpected symlink.');
const entry = new FileBlob({ data: source, mode });
let entry: File;
if (isSymbolicLink(mode)) {
entry = new FileFsRef({ fsPath, mode });
} else {
entry = new FileBlob({ data: source, mode });
}
fsCache.set(relPath, entry);
sourceCache.set(relPath, source);
return source.toString();
@@ -294,16 +301,19 @@ export async function build({
});
console.log('running user script...');
const runScriptTime = Date.now();
await runPackageJsonScript(entrypointFsDirname, 'now-build', spawnOpts);
console.log(`script complete [${Date.now() - runScriptTime}ms]`);
console.log('tracing input files...');
const traceTime = Date.now();
const { preparedFiles, shouldAddSourcemapSupport, watch } = await compile(
workPath,
entrypointPath,
entrypoint,
config,
meta
config
);
console.log(`trace complete [${Date.now() - traceTime}ms]`);
const launcherFiles: Files = {
[`${LAUNCHER_FILENAME}.js`]: new FileBlob({

View File

@@ -453,11 +453,18 @@ function fixConfig(ts: TSCommon, config: _ts.ParsedCommandLine) {
delete config.options.tsBuildInfoFile;
delete config.options.incremental;
// Target ES5 output by default (instead of ES3).
// Target esnext output by default (instead of ES3).
// This will prevent TS from polyfill/downlevel emit.
if (config.options.target === undefined) {
config.options.target = ts.ScriptTarget.ESNext;
}
// When mixing TS with JS, its best to enable this flag.
// This is useful when no `tsconfig.json` is supplied.
if (config.options.esModuleInterop === undefined) {
config.options.esModuleInterop = true;
}
// Target CommonJS, always!
config.options.module = ts.ModuleKind.CommonJS;

View File

@@ -3,13 +3,15 @@ import { parse } from 'url';
const func = (req: IncomingMessage, res: ServerResponse) => {
if (req.url) {
const url = parse(req.url);
const { pathname, search } = parse(req.url);
const location = pathname
? pathname.replace(/\/+/g, '/') + (search ? search : '')
: '/';
/*
res.writeHead(302, {
Location: url.pathname
? url.pathname.replace(/\/+/g, '/') + (url.search ? url.search : '')
: '/',
});
res.end();
Location: location
});*/
res.end(`double-redirect:RANDOMNESS_PLACEHOLDER:${location}`);
}
};

View File

@@ -29,7 +29,7 @@
"mustContain": "root:RANDOMNESS_PLACEHOLDER"
},
{
"path": "//",
"path": "/pricing/",
"mustContain": "trailing-redirect:RANDOMNESS_PLACEHOLDER"
}
]

View File

@@ -0,0 +1,8 @@
//@ts-ignore test will compile during deployment
import express from 'express';
const router = express.Router();
export default function handler(req: any, res: any) {
if (router && req) res.end('default-import:RANDOMNESS_PLACEHOLDER');
else res.end('failed to fetch default import');
}

View File

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

View File

@@ -0,0 +1,6 @@
{
"dependencies": {
"@types/express": "4.17.0",
"express": "4.17.1"
}
}

View File

@@ -0,0 +1,11 @@
import { dep1 } from './js/ecmascript-module';
const { dep2 } = require('./js/commonjs-module');
module.exports = (req, res) => {
if (req && typeof dep1 === 'string' && typeof dep2 === 'string') {
res.end('mixed-modules:js:RANDOMNESS_PLACEHOLDER');
} else {
res.end('import failed');
}
};

View File

@@ -0,0 +1,11 @@
import { IncomingMessage, ServerResponse } from 'http';
import { dep1 } from './ts/ecmascript-module';
const { dep2 } = require('./ts/commonjs-module');
module.exports = (req: IncomingMessage, res: ServerResponse) => {
if (req && typeof dep1 === 'string' && typeof dep2 === 'string') {
res.end('mixed-modules:ts:RANDOMNESS_PLACEHOLDER');
} else {
res.end('import failed');
}
};

View File

@@ -0,0 +1 @@
module.exports = { dep2: 'dep2' };

View File

@@ -0,0 +1,2 @@
export const dep1 = 'dep1';
export const another = 'another';

View File

@@ -0,0 +1,5 @@
{
"engines": {
"node": "10.x"
}
}

View File

@@ -0,0 +1,14 @@
{
"version": 2,
"builds": [{ "src": "entrypoint**", "use": "@now/node" }],
"probes": [
{
"path": "/entrypoint.js",
"mustContain": "mixed-modules:js:RANDOMNESS_PLACEHOLDER"
},
{
"path": "/entrypoint.ts",
"mustContain": "mixed-modules:ts:RANDOMNESS_PLACEHOLDER"
}
]
}

View File

@@ -0,0 +1 @@
module.exports = { dep2: 'dep2' };

View File

@@ -0,0 +1 @@
export const dep1 = 'dep1';

View File

@@ -0,0 +1,5 @@
{
"engines": {
"node": "10.x"
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@builders-typescript-test/lib",
"main": "index.ts",
"main": "src/index",
"version": "1.0.0",
"devDependencies": {
"typescript": "^3.4.5"

View File

@@ -1,4 +1,4 @@
{
"extends": "../../tsconfig.json",
"include": ["./**/*.ts"]
"include": ["src/**/*.ts"]
}

View File

@@ -1,6 +1,6 @@
{
"name": "@now/python",
"version": "0.2.12",
"version": "0.2.13",
"main": "./dist/index.js",
"license": "MIT",
"homepage": "https://zeit.co/docs/v2/deployments/official-builders/python-now-python",

View File

@@ -71,14 +71,14 @@ async function pipInstallUser(pipPath: string, ...args: string[]) {
}
async function pipenvInstall(pyUserBase: string, srcDir: string) {
console.log('running "pipenv_to_requirements -f');
console.log('running pipfile2req');
try {
await execa(join(pyUserBase, 'bin', 'pipenv_to_requirements'), ['-f'], {
const out = await execa.stdout(join(pyUserBase, 'bin', 'pipfile2req'), [], {
cwd: srcDir,
stdio: 'inherit',
});
fs.writeFileSync(join(srcDir, 'requirements.txt'), out);
} catch (err) {
console.log('failed to run "pipenv_to_requirements -f"');
console.log('failed to run "pipfile2req"');
throw err;
}
}
@@ -145,8 +145,7 @@ export const build = async ({
console.log('found "Pipfile.lock"');
// Install pipenv.
await pipInstallUser(pipPath, ' pipenv_to_requirements');
await pipInstallUser(pipPath, 'pipfile-requirements', '--no-warn-script-location');
await pipenvInstall(pyUserBase, pipfileLockDir);
}

1
packages/now-routing-utils/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/dist

View File

@@ -0,0 +1,20 @@
# @now/routing-utils
Route validation utilities
## Usage
`yarn add @now/routing-utils`
exports.normalizeRoutes:
`(routes: Array<Route> | null) => { routes: Array<Route> | null; error: NowError | null }`
exports.schema:
```js
const ajv = new Ajv();
const validate = ajv.compile(schema);
const valid = validate([{ src: '/about', dest: '/about.html' }]);
if (!valid) console.log(validate.errors);
```

View File

@@ -0,0 +1,29 @@
{
"name": "@now/routing-utils",
"version": "1.2.1",
"description": "ZEIT Now route validation utilities",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"files": [
"dist"
],
"repository": {
"type": "git",
"url": "https://github.com/zeit/now-builders.git",
"directory": "packages/now-routing-utils"
},
"license": "MIT",
"scripts": {
"prepublishOnly": "tsc",
"build": "tsc",
"watch": "tsc --watch",
"test": "tsc && jest"
},
"devDependencies": {
"ajv": "^6.0.0",
"typescript": "3.5.2"
},
"optionalDependencies": {
"ajv": "^6.0.0"
}
}

View File

@@ -0,0 +1,175 @@
export type NowError = {
code: string;
message: string;
errors: {
message: string;
src?: string;
handle?: string;
}[];
sha?: string; // File errors
};
export type Source = {
src: string;
dest?: string;
headers?: {};
methods?: string[];
continue?: boolean;
status?: number;
};
export type Handler = {
handle: string;
};
export type Route = Source | Handler;
export function isHandler(route: Route): route is Handler {
return typeof (<Handler>route).handle !== 'undefined';
}
export function normalizeRoutes(
inputRoutes: Array<Route> | null
): { routes: Array<Route> | null; error: NowError | null } {
if (!inputRoutes || inputRoutes.length === 0) {
return { routes: inputRoutes, error: null };
}
const routes: Route[] = [];
const handling: string[] = [];
const errors = [];
// We don't want to treat the input routes as references
inputRoutes.forEach(r => routes.push(Object.assign({}, r)));
for (const route of routes) {
if (isHandler(route)) {
// typeof { handle: string }
if (Object.keys(route).length !== 1) {
errors.push({
message: `Cannot have any other keys when handle is used (handle: ${
route.handle
})`,
handle: route.handle,
});
}
if (!['filesystem'].includes(route.handle)) {
errors.push({
message: `This is not a valid handler (handle: ${route.handle})`,
handle: route.handle,
});
}
if (handling.includes(route.handle)) {
errors.push({
message: `You can only handle something once (handle: ${
route.handle
})`,
handle: route.handle,
});
} else {
handling.push(route.handle);
}
} else if (route.src) {
// typeof normal route
if (route.continue && route.dest) {
errors.push({
message: `Cannot use both continue and dest`,
src: route.src,
});
}
// Route src should always start with a '^'
if (!route.src.startsWith('^')) {
route.src = `^${route.src}`;
}
// Route src should always end with a '$'
if (!route.src.endsWith('$')) {
route.src = `${route.src}$`;
}
try {
// This feels a bit dangerous if there would be a vulnerability in RegExp.
new RegExp(route.src);
} catch (err) {
errors.push({
message: `Invalid regular expression: "${route.src}"`,
src: route.src,
});
}
} else {
errors.push({
message: 'A route must set either handle or src',
});
}
}
const error =
errors.length > 0
? {
code: 'invalid_routes',
message: `One or more invalid routes were found: \n${JSON.stringify(
errors,
null,
2
)}`,
errors,
}
: null;
return { routes, error };
}
/**
* An ajv schema for the routes array
*/
export const schema = {
type: 'array',
maxItems: 1024,
items: {
type: 'object',
additionalProperties: false,
properties: {
src: {
type: 'string',
maxLength: 4096,
},
dest: {
type: 'string',
maxLength: 4096,
},
methods: {
type: 'array',
maxItems: 10,
items: {
type: 'string',
maxLength: 32,
},
},
headers: {
type: 'object',
additionalProperties: false,
minProperties: 1,
maxProperties: 100,
patternProperties: {
'^.{1,256}$': {
type: 'string',
maxLength: 4096,
},
},
},
handle: {
type: 'string',
maxLength: 32,
},
continue: {
type: 'boolean',
},
status: {
type: 'integer',
minimum: 100,
maximum: 999,
},
},
},
};

View File

@@ -0,0 +1,406 @@
const assert = require('assert');
const Ajv = require('ajv');
const { normalizeRoutes, isHandler, schema } = require('../dist');
const ajv = new Ajv();
const assertValid = (routes) => {
const validate = ajv.compile(schema);
const valid = validate(routes);
if (!valid) console.log(validate.errors);
assert.equal(valid, true);
};
const assertError = (routes, errors) => {
const validate = ajv.compile(schema);
const valid = validate(routes);
assert.equal(valid, false);
assert.deepEqual(validate.errors, errors);
};
describe('normalizeRoutes', () => {
test('accepts valid routes', () => {
const routes = [
{ src: '^/about$' },
{
src: '^/blog$',
methods: ['GET'],
headers: { 'Cache-Control': 'no-cache' },
dest: '/blog',
},
{ handle: 'filesystem' },
{ src: '^/(?<slug>[^/]+)$', dest: 'blog?slug=$slug' },
];
assertValid(routes);
const normalized = normalizeRoutes(routes);
assert.equal(normalized.error, null);
assert.deepStrictEqual(normalized.routes, routes);
});
test('normalizes src', () => {
const expected = '^/about$';
const expected2 = '^\\/about$';
const sources = [
{ src: '/about' },
{ src: '/about$' },
{ src: '\\/about' },
{ src: '\\/about$' },
{ src: '^/about' },
{ src: '^/about$' },
{ src: '^\\/about' },
{ src: '^\\/about$' },
];
assertValid(sources);
const normalized = normalizeRoutes(sources);
assert.equal(normalized.error, null);
assert.notEqual(normalized.routes, null);
if (normalized.routes) {
normalized.routes.forEach((route) => {
if (isHandler(route)) {
assert.fail(
`Normalizer returned: { handle: ${
route.handle
} } instead of { src: ${expected} }`,
);
} else {
assert.ok(route.src === expected || route.src === expected2);
}
});
}
});
test('returns if null', () => {
const input = null;
const { error, routes } = normalizeRoutes(input);
assert.strictEqual(error, null);
assert.strictEqual(routes, input);
});
test('returns if empty', () => {
const input = [];
const { error, routes } = normalizeRoutes(input);
assert.strictEqual(error, null);
assert.strictEqual(routes, input);
});
test('fails with abnormal routes', () => {
const errors = [];
const routes = [];
routes.push({ handle: 'doesnotexist' });
errors.push({
message: 'This is not a valid handler (handle: doesnotexist)',
handle: 'doesnotexist',
});
// @ts-ignore
routes.push({ handle: 'filesystem', illegal: true });
errors.push({
message:
'Cannot have any other keys when handle is used (handle: filesystem)',
handle: 'filesystem',
});
routes.push({ handle: 'filesystem' });
errors.push({
message: 'You can only handle something once (handle: filesystem)',
handle: 'filesystem',
});
routes.push({ src: '^/about$', dest: '/about', continue: true });
errors.push({
message: 'Cannot use both continue and dest',
src: '^/about$',
});
routes.push({ src: '^/(broken]$' });
errors.push({
message: 'Invalid regular expression: "^/(broken]$"',
src: '^/(broken]$',
});
// @ts-ignore
routes.push({ doesNotExist: true });
errors.push({
message: 'A route must set either handle or src',
});
// @ts-ignore
routes.push({ src: '^/about$', doesNotExist: true });
const normalized = normalizeRoutes(routes);
assert.deepStrictEqual(normalized.routes, routes);
assert.deepStrictEqual(normalized.error, {
code: 'invalid_routes',
message: `One or more invalid routes were found: \n${JSON.stringify(
errors,
null,
2,
)}`,
errors,
});
});
test('fails if over 1024 routes', () => {
// @ts-ignore
assertError('string', [
{
dataPath: '',
keyword: 'type',
message: 'should be array',
params: {
type: 'array',
},
schemaPath: '#/type',
},
]);
const arr = new Array(1026);
arr.fill(true);
// @ts-ignore
assertError(arr, [
{
dataPath: '',
keyword: 'maxItems',
message: 'should NOT have more than 1024 items',
params: {
limit: '1024',
},
schemaPath: '#/maxItems',
},
]);
});
test('fails is src is not string', () => {
assertError(
[
// @ts-ignore
{
src: false,
},
],
[
{
dataPath: '[0].src',
keyword: 'type',
message: 'should be string',
params: {
type: 'string',
},
schemaPath: '#/items/properties/src/type',
},
],
);
});
test('fails if dest is not string', () => {
assertError(
[
// @ts-ignore
{
dest: false,
},
],
[
{
dataPath: '[0].dest',
keyword: 'type',
message: 'should be string',
params: {
type: 'string',
},
schemaPath: '#/items/properties/dest/type',
},
],
);
});
test('fails if methods is not array', () => {
assertError(
[
// @ts-ignore
{
methods: false,
},
],
[
{
dataPath: '[0].methods',
keyword: 'type',
message: 'should be array',
params: {
type: 'array',
},
schemaPath: '#/items/properties/methods/type',
},
],
);
});
test('fails if methods is not string', () => {
assertError(
[
// @ts-ignore
{
methods: [false],
},
],
[
{
dataPath: '[0].methods[0]',
keyword: 'type',
message: 'should be string',
params: {
type: 'string',
},
schemaPath: '#/items/properties/methods/items/type',
},
],
);
});
test('fails if headers is not an object', () => {
assertError(
[
// @ts-ignore
{
headers: false,
},
],
[
{
dataPath: '[0].headers',
keyword: 'type',
message: 'should be object',
params: {
type: 'object',
},
schemaPath: '#/items/properties/headers/type',
},
],
);
});
test('fails if header is not a string', () => {
assertError(
[
// @ts-ignore
{
headers: {
test: false,
},
},
],
[
{
dataPath: "[0].headers['test']",
keyword: 'type',
message: 'should be string',
params: {
type: 'string',
},
schemaPath:
'#/items/properties/headers/patternProperties/%5E.%7B1%2C256%7D%24/type',
},
],
);
});
test('fails if handle is not string', () => {
assertError(
[
// @ts-ignore
{
handle: false,
},
],
[
{
dataPath: '[0].handle',
keyword: 'type',
message: 'should be string',
params: {
type: 'string',
},
schemaPath: '#/items/properties/handle/type',
},
],
);
});
test('fails if continue is not boolean', () => {
assertError(
[
// @ts-ignore
{
continue: 'false',
},
],
[
{
dataPath: '[0].continue',
keyword: 'type',
message: 'should be boolean',
params: {
type: 'boolean',
},
schemaPath: '#/items/properties/continue/type',
},
],
);
});
test('fails if status is not number', () => {
assertError(
[
// @ts-ignore
{
status: '404',
},
],
[
{
dataPath: '[0].status',
keyword: 'type',
message: 'should be integer',
params: {
type: 'integer',
},
schemaPath: '#/items/properties/status/type',
},
],
);
});
test('fails if property does not exist', () => {
assertError(
[
{
// @ts-ignore
doesNotExist: false,
},
],
[
{
dataPath: '[0]',
keyword: 'additionalProperties',
message: 'should NOT have additional properties',
params: {
additionalProperty: 'doesNotExist',
},
schemaPath: '#/items/additionalProperties',
},
],
);
});
});

View File

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

View File

@@ -1,6 +1,6 @@
{
"name": "@now/rust",
"version": "0.2.9",
"version": "0.2.10",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://zeit.co/docs/v2/deployments/official-builders/now-rust",
@@ -13,7 +13,8 @@
"build": "tsc",
"test": "tsc && jest",
"prepublish": "tsc",
"typecheck": "tsc --noEmit"
"typecheck": "tsc --noEmit",
"now-postinstall": "node dist/install.js"
},
"files": [
"dist",

View File

@@ -14,7 +14,6 @@ import {
DownloadedFiles,
Lambda,
} from '@now/build-utils'; // eslint-disable-line import/no-extraneous-dependencies
import installRust from './install-rust';
interface PackageManifest {
targets: { kind: string; name: string }[];
@@ -275,10 +274,6 @@ export async function build(opts: BuildOptions) {
const downloadedFiles = await download(files, workPath, meta);
const entryPath = downloadedFiles[entrypoint].fsPath;
if (!meta.isDev) {
await installRust(config.rust);
}
const { PATH, HOME } = process.env;
const rustEnv: Record<string, string> = {
...process.env,

View File

@@ -0,0 +1,6 @@
import installRust from './install-rust';
installRust().catch(err => {
console.error(err);
process.exit(1);
});

View File

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

View File

@@ -1,6 +1,6 @@
{
"name": "@now/static-build",
"version": "0.7.3",
"version": "0.9.3",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://zeit.co/docs/v2/deployments/official-builders/static-build-now-static-build",
@@ -13,17 +13,15 @@
"directory": "packages/now-static-build"
},
"scripts": {
"build": "tsc",
"test": "tsc && jest",
"prepublishOnly": "tsc"
},
"dependencies": {
"cross-spawn": "6.0.5",
"get-port": "5.0.0",
"promise-timeout": "1.3.0"
"build": "./build.sh",
"test": "./build.sh && jest",
"prepublishOnly": "./build.sh"
},
"devDependencies": {
"@types/promise-timeout": "1.3.0",
"cross-spawn": "6.0.5",
"get-port": "5.0.0",
"promise-timeout": "1.3.0",
"typescript": "3.5.2"
}
}

View File

@@ -27,6 +27,11 @@ export default [
dependency: 'hexo',
getOutputDirName: async () => 'public',
},
{
name: 'Docusaurus 2.0',
dependency: '@docusaurus/core',
getOutputDirName: async () => 'build',
},
{
name: 'Preact',
dependency: 'preact-cli',
@@ -41,6 +46,20 @@ export default [
},
],
},
{
name: 'Ember',
dependency: 'ember-cli',
getOutputDirName: async () => 'dist',
defaultRoutes: [
{
handle: 'filesystem',
},
{
src: '/(.*)',
dest: '/index.html',
},
],
},
{
name: 'Vue.js',
dependency: '@vue/cli-service',
@@ -193,7 +212,7 @@ export default [
],
},
{
name: 'Docusaurus',
name: 'Docusaurus 1.0',
dependency: 'docusaurus',
getOutputDirName: async (dirPrefix: string) => {
const base = 'build';
@@ -208,4 +227,9 @@ export default [
return base;
},
},
{
name: 'Sapper',
dependency: 'sapper',
getOutputDirName: async () => '__sapper__/export',
},
];

View File

@@ -38,39 +38,45 @@ interface Framework {
minNodeRange?: string;
}
function validateDistDir(distDir: string, isDev: boolean | undefined) {
function validateDistDir(
distDir: string,
isDev: boolean | undefined,
config: Config
) {
const distDirName = path.basename(distDir);
const exists = () => existsSync(distDir);
const isDirectory = () => statSync(distDir).isDirectory();
const isEmpty = () => readdirSync(distDir).length === 0;
const hash = isDev
? '#local-development'
: '#configuring-the-build-output-directory';
const docsUrl = `https://zeit.co/docs/v2/deployments/official-builders/static-build-now-static-build${hash}`;
const distDirName = path.basename(distDir);
if (!existsSync(distDir)) {
const message =
`Build was unable to create the distDir: "${distDirName}".` +
`\nMake sure you configure the the correct distDir: ${docsUrl}`;
throw new Error(message);
}
const stat = statSync(distDir);
if (!stat.isDirectory()) {
const message =
`Build failed because distDir is not a directory: "${distDirName}".` +
`\nMake sure you configure the the correct distDir: ${docsUrl}`;
throw new Error(message);
const info = config.zeroConfig
? '\nMore details: https://zeit.co/docs/v2/advanced/platform/frequently-asked-questions#missing-public-directory'
: `\nMake sure you configure the the correct distDir: ${docsUrl}`;
if (!exists()) {
throw new Error(`No output directory named "${distDirName}" found.${info}`);
}
const contents = readdirSync(distDir);
if (contents.length === 0) {
const message =
`Build failed because distDir is empty: "${distDirName}".` +
`\nMake sure you configure the the correct distDir: ${docsUrl}`;
throw new Error(message);
if (!isDirectory()) {
throw new Error(
`Build failed because distDir is not a directory: "${distDirName}".${info}`
);
}
if (isEmpty()) {
throw new Error(
`Build failed because distDir is empty: "${distDirName}".${info}`
);
}
}
function getCommand(pkg: PackageJson, cmd: string, config: Config) {
function getCommand(pkg: PackageJson, cmd: string, { zeroConfig }: Config) {
// The `dev` script can be `now dev`
const nowCmd = `now-${cmd}`;
const { zeroConfig } = config;
if (!zeroConfig && cmd === 'dev') {
return nowCmd;
@@ -86,7 +92,7 @@ function getCommand(pkg: PackageJson, cmd: string, config: Config) {
return cmd;
}
return nowCmd;
return zeroConfig ? cmd : nowCmd;
}
export const version = 2;
@@ -139,6 +145,9 @@ export async function build({
const devScript = getCommand(pkg, 'dev', config as Config);
if (config.zeroConfig) {
// `public` is the default for zero config
distPath = path.join(workPath, path.dirname(entrypoint), 'public');
const dependencies = Object.assign(
{},
pkg.dependencies,
@@ -291,7 +300,7 @@ export async function build({
distPath = path.join(outputDirPrefix, outputDirName);
}
validateDistDir(distPath, meta.isDev);
validateDistDir(distPath, meta.isDev, config);
output = await glob('**', distPath, mountpoint);
if (framework && framework.defaultRoutes) {
@@ -308,7 +317,7 @@ export async function build({
const nodeVersion = await getNodeVersion(entrypointDir);
const spawnOpts = getSpawnOptions(meta, nodeVersion);
await runShellScript(path.join(workPath, entrypoint), [], spawnOpts);
validateDistDir(distPath, meta.isDev);
validateDistDir(distPath, meta.isDev, config);
return glob('**', distPath, mountpoint);
}

View File

@@ -3,6 +3,6 @@
"cowsay": "^1.3.1"
},
"scripts": {
"build": "mkdir dist && cowsay cow:RANDOMNESS_PLACEHOLDER > dist/index.txt"
"build": "mkdir public && cowsay cow:RANDOMNESS_PLACEHOLDER > public/index.txt"
}
}

View File

@@ -3,6 +3,6 @@
"yodasay": "^1.1.6"
},
"scripts": {
"build": "mkdir dist && yodasay yoda:RANDOMNESS_PLACEHOLDER > dist/index.txt"
"build": "mkdir public && yodasay yoda:RANDOMNESS_PLACEHOLDER > public/index.txt"
}
}

View File

@@ -0,0 +1,20 @@
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# editorconfig.org
root = true
[*]
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
indent_style = space
indent_size = 2
[*.hbs]
insert_final_newline = false
[*.{diff,md}]
trim_trailing_whitespace = false

View File

@@ -0,0 +1,9 @@
{
/**
Ember CLI sends analytics information by default. The data is completely
anonymous, but there are times when you might want to disable this behavior.
Setting `disableAnalytics` to true will prevent any data from being sent.
*/
"disableAnalytics": false
}

View File

@@ -0,0 +1,20 @@
# unconventional js
/blueprints/*/files/
/vendor/
# compiled output
/dist/
/tmp/
# dependencies
/bower_components/
/node_modules/
# misc
/coverage/
!.*
# ember-try
/.node_modules.ember-try/
/bower.json.ember-try
/package.json.ember-try

View File

@@ -0,0 +1,47 @@
module.exports = {
root: true,
parserOptions: {
ecmaVersion: 2018,
sourceType: 'module',
},
plugins: ['ember'],
extends: ['eslint:recommended', 'plugin:ember/recommended'],
env: {
browser: true,
},
rules: {},
overrides: [
// node files
{
files: [
'.eslintrc.js',
'.template-lintrc.js',
'ember-cli-build.js',
'testem.js',
'blueprints/*/index.js',
'config/**/*.js',
'lib/*/index.js',
'server/**/*.js',
],
parserOptions: {
sourceType: 'script',
},
env: {
browser: false,
node: true,
},
plugins: ['node'],
rules: Object.assign(
{},
require('eslint-plugin-node').configs.recommended.rules,
{
// add your custom rules and overrides for node files here
// this can be removed once the following is fixed
// https://github.com/mysticatea/eslint-plugin-node/issues/77
'node/no-unpublished-require': 'off',
}
),
},
],
};

View File

@@ -0,0 +1,25 @@
# See https://help.github.com/ignore-files/ for more about ignoring files.
# compiled output
/dist/
/tmp/
# dependencies
/bower_components/
/node_modules/
# misc
/.env*
/.pnp*
/.sass-cache
/connect.lock
/coverage/
/libpeerconnection.log
/npm-debug.log*
/testem.log
/yarn-error.log
# ember-try
/.node_modules.ember-try/
/bower.json.ember-try
/package.json.ember-try

View File

@@ -0,0 +1,5 @@
'use strict';
module.exports = {
extends: 'recommended',
};

View File

@@ -0,0 +1,24 @@
---
language: node_js
node_js:
- "8"
sudo: false
dist: trusty
addons:
chrome: stable
cache:
directories:
- $HOME/.npm
env:
global:
# See https://git.io/vdao3 for details.
- JOBS=1
script:
- npm run lint:hbs
- npm run lint:js
- npm test

View File

@@ -0,0 +1,3 @@
{
"ignore_dirs": ["tmp", "dist"]
}

View File

@@ -0,0 +1,57 @@
# site
This README outlines the details of collaborating on this Ember application.
A short introduction of this app could easily go here.
## Prerequisites
You will need the following things properly installed on your computer.
- [Git](https://git-scm.com/)
- [Node.js](https://nodejs.org/) (with npm)
- [Ember CLI](https://ember-cli.com/)
- [Google Chrome](https://google.com/chrome/)
## Installation
- `git clone <repository-url>` this repository
- `cd site`
- `npm install`
## Running / Development
- `ember serve`
- Visit your app at [http://localhost:4200](http://localhost:4200).
- Visit your tests at [http://localhost:4200/tests](http://localhost:4200/tests).
### Code Generators
Make use of the many generators for code, try `ember help generate` for more details
### Running Tests
- `ember test`
- `ember test --server`
### Linting
- `npm run lint:hbs`
- `npm run lint:js`
- `npm run lint:js -- --fix`
### Building
- `ember build` (development)
- `ember build --environment production` (production)
### Deploying
Specify what it takes to deploy your app.
## Further Reading / Useful Links
- [ember.js](https://emberjs.com/)
- [ember-cli](https://ember-cli.com/)
- Development Browser Extensions
- [ember inspector for chrome](https://chrome.google.com/webstore/detail/ember-inspector/bmdblncegkenkacieihfhpjfppoconhi)
- [ember inspector for firefox](https://addons.mozilla.org/en-US/firefox/addon/ember-inspector/)

View File

@@ -0,0 +1,14 @@
import Application from '@ember/application';
import Resolver from './resolver';
import loadInitializers from 'ember-load-initializers';
import config from './config/environment';
const App = Application.extend({
modulePrefix: config.modulePrefix,
podModulePrefix: config.podModulePrefix,
Resolver,
});
loadInitializers(App, config.modulePrefix);
export default App;

View File

@@ -0,0 +1,25 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Site</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
{{content-for "head"}}
<link integrity="" rel="stylesheet" href="{{rootURL}}assets/vendor.css">
<link integrity="" rel="stylesheet" href="{{rootURL}}assets/site.css">
{{content-for "head-footer"}}
</head>
<body>
{{content-for "body"}}
<script src="{{rootURL}}assets/vendor.js"></script>
<script src="{{rootURL}}assets/site.js"></script>
{{content-for "body-footer"}}
</body>
</html>

View File

@@ -0,0 +1,3 @@
import Resolver from 'ember-resolver';
export default Resolver;

Some files were not shown because too many files have changed in this diff Show More