mirror of
https://github.com/kevin-DL/sapper.git
synced 2026-01-20 14:25:07 +00:00
8
.gitignore
vendored
8
.gitignore
vendored
@@ -4,3 +4,11 @@ cypress/screenshots
|
|||||||
test/app/.sapper
|
test/app/.sapper
|
||||||
runtime.js
|
runtime.js
|
||||||
yarn.lock
|
yarn.lock
|
||||||
|
cli.js
|
||||||
|
cli.js.map
|
||||||
|
middleware.js
|
||||||
|
middleware.js.map
|
||||||
|
core.js
|
||||||
|
core.js.map
|
||||||
|
webpack/config.js
|
||||||
|
webpack/config.js.map
|
||||||
47
lib/build.js
47
lib/build.js
@@ -1,47 +0,0 @@
|
|||||||
process.env.NODE_ENV = 'production';
|
|
||||||
|
|
||||||
const fs = require('fs');
|
|
||||||
const path = require('path');
|
|
||||||
const mkdirp = require('mkdirp');
|
|
||||||
const rimraf = require('rimraf');
|
|
||||||
const { client, server } = require('./utils/compilers.js');
|
|
||||||
const create_app = require('./utils/create_app.js');
|
|
||||||
const generate_asset_cache = require('./utils/generate_asset_cache.js');
|
|
||||||
const { dest } = require('./config.js');
|
|
||||||
|
|
||||||
module.exports = () => {
|
|
||||||
mkdirp.sync(dest);
|
|
||||||
rimraf.sync(path.join(dest, '**/*'));
|
|
||||||
|
|
||||||
// create main.js and server-routes.js
|
|
||||||
create_app();
|
|
||||||
|
|
||||||
return new Promise((fulfil, reject) => {
|
|
||||||
function handleErrors(err, stats) {
|
|
||||||
if (err) {
|
|
||||||
reject(err);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stats.hasErrors()) {
|
|
||||||
console.error(stats.toString({ colors: true }));
|
|
||||||
reject(new Error(`Encountered errors while building app`));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
client.run((err, clientStats) => {
|
|
||||||
handleErrors(err, clientStats);
|
|
||||||
const clientInfo = clientStats.toJson();
|
|
||||||
fs.writeFileSync(path.join(dest, 'stats.client.json'), JSON.stringify(clientInfo, null, ' '));
|
|
||||||
|
|
||||||
server.run((err, serverStats) => {
|
|
||||||
handleErrors(err, serverStats);
|
|
||||||
const serverInfo = serverStats.toJson();
|
|
||||||
fs.writeFileSync(path.join(dest, 'stats.server.json'), JSON.stringify(serverInfo, null, ' '));
|
|
||||||
|
|
||||||
generate_asset_cache(clientInfo, serverInfo);
|
|
||||||
fulfil();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
const path = require('path');
|
|
||||||
const mkdirp = require('mkdirp');
|
|
||||||
const rimraf = require('rimraf');
|
|
||||||
|
|
||||||
exports.dev = process.env.NODE_ENV !== 'production';
|
|
||||||
|
|
||||||
exports.templates = path.resolve(process.env.SAPPER_TEMPLATES || 'templates');
|
|
||||||
|
|
||||||
exports.src = path.resolve(process.env.SAPPER_ROUTES || 'routes');
|
|
||||||
|
|
||||||
exports.dest = path.resolve(process.env.SAPPER_DEST || '.sapper');
|
|
||||||
|
|
||||||
if (exports.dev) {
|
|
||||||
mkdirp.sync(exports.dest);
|
|
||||||
rimraf.sync(path.join(exports.dest, '**/*'));
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.entry = {
|
|
||||||
client: path.resolve(exports.templates, '.main.rendered.js'),
|
|
||||||
server: path.resolve(exports.dest, 'server-entry.js')
|
|
||||||
};
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
const glob = require('glob');
|
|
||||||
const create_routes = require('./utils/create_routes.js');
|
|
||||||
const { src, dev } = require('./config.js');
|
|
||||||
|
|
||||||
const callbacks = [];
|
|
||||||
|
|
||||||
exports.onchange = fn => {
|
|
||||||
callbacks.push(fn);
|
|
||||||
};
|
|
||||||
|
|
||||||
function update() {
|
|
||||||
exports.routes = create_routes(
|
|
||||||
glob.sync('**/*.+(html|js|mjs)', { cwd: src })
|
|
||||||
);
|
|
||||||
|
|
||||||
callbacks.forEach(fn => fn());
|
|
||||||
}
|
|
||||||
|
|
||||||
update();
|
|
||||||
|
|
||||||
if (dev) {
|
|
||||||
const watcher = require('chokidar').watch(`${src}/**/*.+(html|js|mjs)`, {
|
|
||||||
ignoreInitial: true,
|
|
||||||
persistent: false
|
|
||||||
});
|
|
||||||
|
|
||||||
watcher.on('add', update);
|
|
||||||
watcher.on('change', update);
|
|
||||||
watcher.on('unlink', update);
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
const path = require('path');
|
|
||||||
const relative = require('require-relative');
|
|
||||||
const webpack = relative('webpack', process.cwd());
|
|
||||||
|
|
||||||
exports.client = webpack(
|
|
||||||
require(path.resolve('webpack.client.config.js'))
|
|
||||||
);
|
|
||||||
|
|
||||||
exports.server = webpack(
|
|
||||||
require(path.resolve('webpack.server.config.js'))
|
|
||||||
);
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
const fs = require('fs');
|
|
||||||
const path = require('path');
|
|
||||||
const route_manager = require('../route_manager.js');
|
|
||||||
const { src, entry, dev } = require('../config.js');
|
|
||||||
|
|
||||||
function posixify(file) {
|
|
||||||
return file.replace(/[/\\]/g, '/');
|
|
||||||
}
|
|
||||||
|
|
||||||
function create_app() {
|
|
||||||
const { routes } = route_manager;
|
|
||||||
|
|
||||||
function create_client_main() {
|
|
||||||
const template = fs.readFileSync('templates/main.js', 'utf-8');
|
|
||||||
|
|
||||||
const code = `[${
|
|
||||||
routes
|
|
||||||
.filter(route => route.type === 'page')
|
|
||||||
.map(route => {
|
|
||||||
const params = route.dynamic.length === 0 ?
|
|
||||||
'{}' :
|
|
||||||
`{ ${route.dynamic.map((part, i) => `${part}: match[${i + 1}]`).join(', ') } }`;
|
|
||||||
|
|
||||||
const file = posixify(`${src}/${route.file}`);
|
|
||||||
return `{ pattern: ${route.pattern}, params: match => (${params}), load: () => import(/* webpackChunkName: "${route.id}" */ '${file}') }`
|
|
||||||
})
|
|
||||||
.join(', ')
|
|
||||||
}]`;
|
|
||||||
|
|
||||||
let main = template
|
|
||||||
.replace(/__app__/g, posixify(path.resolve(__dirname, '../../runtime/app.js')))
|
|
||||||
.replace(/__routes__/g, code)
|
|
||||||
.replace(/__dev__/g, String(dev));
|
|
||||||
|
|
||||||
if (dev) {
|
|
||||||
const hmr_client = posixify(require.resolve(`webpack-hot-middleware/client`));
|
|
||||||
main += `\n\nimport('${hmr_client}?path=/__webpack_hmr&timeout=20000'); if (module.hot) module.hot.accept();`
|
|
||||||
}
|
|
||||||
|
|
||||||
fs.writeFileSync(entry.client, main);
|
|
||||||
|
|
||||||
// need to fudge the mtime, because webpack is soft in the head
|
|
||||||
const { atime, mtime } = fs.statSync(entry.client);
|
|
||||||
fs.utimesSync(entry.client, new Date(atime.getTime() - 999999), new Date(mtime.getTime() - 999999));
|
|
||||||
}
|
|
||||||
|
|
||||||
function create_server_routes() {
|
|
||||||
const imports = routes
|
|
||||||
.map(route => {
|
|
||||||
const file = posixify(`${src}/${route.file}`);
|
|
||||||
return route.type === 'page' ?
|
|
||||||
`import ${route.id} from '${file}';` :
|
|
||||||
`import * as ${route.id} from '${file}';`;
|
|
||||||
})
|
|
||||||
.join('\n');
|
|
||||||
|
|
||||||
const exports = `export { ${routes.map(route => route.id)} };`;
|
|
||||||
|
|
||||||
fs.writeFileSync(entry.server, `${imports}\n\n${exports}`);
|
|
||||||
|
|
||||||
const { atime, mtime } = fs.statSync(entry.server);
|
|
||||||
fs.utimesSync(entry.server, new Date(atime.getTime() - 999999), new Date(mtime.getTime() - 999999));
|
|
||||||
}
|
|
||||||
|
|
||||||
create_client_main();
|
|
||||||
create_server_routes();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dev) {
|
|
||||||
route_manager.onchange(create_app);
|
|
||||||
|
|
||||||
const watcher = require('chokidar').watch(`templates/main.js`, {
|
|
||||||
ignoreInitial: true,
|
|
||||||
persistent: false
|
|
||||||
});
|
|
||||||
|
|
||||||
watcher.on('add', create_app);
|
|
||||||
watcher.on('change', create_app);
|
|
||||||
watcher.on('unlink', create_app);
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = create_app;
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
--require source-map-support/register
|
||||||
--recursive
|
--recursive
|
||||||
test/unit/**/*.js
|
test/unit/**/*.js
|
||||||
test/common/test.js
|
test/common/test.js
|
||||||
577
package-lock.json
generated
577
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
17
package.json
17
package.json
@@ -2,13 +2,13 @@
|
|||||||
"name": "sapper",
|
"name": "sapper",
|
||||||
"version": "0.5.1",
|
"version": "0.5.1",
|
||||||
"description": "Military-grade apps, engineered by Svelte",
|
"description": "Military-grade apps, engineered by Svelte",
|
||||||
"main": "lib/index.js",
|
"main": "middleware.js",
|
||||||
"bin": {
|
"bin": {
|
||||||
"sapper": "cli/index.js"
|
"sapper": "cli/index.js"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"cli",
|
"cli.js",
|
||||||
"lib",
|
"middleware.js",
|
||||||
"runtime",
|
"runtime",
|
||||||
"runtime.js",
|
"runtime.js",
|
||||||
"webpack"
|
"webpack"
|
||||||
@@ -22,8 +22,11 @@
|
|||||||
"chokidar": "^1.7.0",
|
"chokidar": "^1.7.0",
|
||||||
"code-frame": "^5.0.0",
|
"code-frame": "^5.0.0",
|
||||||
"escape-html": "^1.0.3",
|
"escape-html": "^1.0.3",
|
||||||
|
"express": "^4.16.2",
|
||||||
|
"glob": "^7.1.2",
|
||||||
"locate-character": "^2.0.5",
|
"locate-character": "^2.0.5",
|
||||||
"mkdirp": "^0.5.1",
|
"mkdirp": "^0.5.1",
|
||||||
|
"node-fetch": "^1.7.3",
|
||||||
"relative": "^3.0.2",
|
"relative": "^3.0.2",
|
||||||
"require-relative": "^0.8.7",
|
"require-relative": "^0.8.7",
|
||||||
"rimraf": "^2.6.2",
|
"rimraf": "^2.6.2",
|
||||||
@@ -35,20 +38,24 @@
|
|||||||
"webpack-hot-middleware": "^2.21.0"
|
"webpack-hot-middleware": "^2.21.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@std/esm": "^0.19.7",
|
||||||
|
"@types/glob": "^5.0.34",
|
||||||
|
"@types/mkdirp": "^0.5.2",
|
||||||
|
"@types/rimraf": "^2.0.2",
|
||||||
"css-loader": "^0.28.7",
|
"css-loader": "^0.28.7",
|
||||||
"eslint": "^4.13.1",
|
"eslint": "^4.13.1",
|
||||||
"eslint-plugin-import": "^2.8.0",
|
"eslint-plugin-import": "^2.8.0",
|
||||||
"express": "^4.16.2",
|
|
||||||
"get-port": "^3.2.0",
|
"get-port": "^3.2.0",
|
||||||
"mocha": "^4.0.1",
|
"mocha": "^4.0.1",
|
||||||
"nightmare": "^2.10.0",
|
"nightmare": "^2.10.0",
|
||||||
"node-fetch": "^1.7.3",
|
|
||||||
"npm-run-all": "^4.1.2",
|
"npm-run-all": "^4.1.2",
|
||||||
"rollup": "^0.53.0",
|
"rollup": "^0.53.0",
|
||||||
"rollup-plugin-typescript": "^0.8.1",
|
"rollup-plugin-typescript": "^0.8.1",
|
||||||
|
"source-map-support": "^0.5.2",
|
||||||
"style-loader": "^0.19.1",
|
"style-loader": "^0.19.1",
|
||||||
"svelte": "^1.49.1",
|
"svelte": "^1.49.1",
|
||||||
"svelte-loader": "^2.3.2",
|
"svelte-loader": "^2.3.2",
|
||||||
|
"ts-node": "^4.1.0",
|
||||||
"tslib": "^1.8.1",
|
"tslib": "^1.8.1",
|
||||||
"typescript": "^2.6.2",
|
"typescript": "^2.6.2",
|
||||||
"wait-on": "^2.0.2"
|
"wait-on": "^2.0.2"
|
||||||
|
|||||||
@@ -1,13 +1,97 @@
|
|||||||
import typescript from 'rollup-plugin-typescript';
|
import typescript from 'rollup-plugin-typescript';
|
||||||
|
import pkg from './package.json';
|
||||||
|
|
||||||
|
const external = [].concat(
|
||||||
|
Object.keys(pkg.dependencies),
|
||||||
|
Object.keys(process.binding('natives')),
|
||||||
|
'sapper/core.js'
|
||||||
|
);
|
||||||
|
|
||||||
|
const paths = {
|
||||||
|
'sapper/core.js': './core.js'
|
||||||
|
};
|
||||||
|
|
||||||
export default [
|
export default [
|
||||||
|
// cli.js
|
||||||
|
{
|
||||||
|
input: 'src/cli/index.ts',
|
||||||
|
output: {
|
||||||
|
file: 'cli.js',
|
||||||
|
format: 'cjs',
|
||||||
|
banner: '#!/usr/bin/env node',
|
||||||
|
paths,
|
||||||
|
sourcemap: true
|
||||||
|
},
|
||||||
|
external,
|
||||||
|
plugins: [
|
||||||
|
typescript({
|
||||||
|
typescript: require('typescript')
|
||||||
|
})
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
// core.js
|
||||||
|
{
|
||||||
|
input: 'src/core/index.ts',
|
||||||
|
output: {
|
||||||
|
file: 'core.js',
|
||||||
|
format: 'cjs',
|
||||||
|
banner: '#!/usr/bin/env node',
|
||||||
|
paths,
|
||||||
|
sourcemap: true
|
||||||
|
},
|
||||||
|
external,
|
||||||
|
plugins: [
|
||||||
|
typescript({
|
||||||
|
typescript: require('typescript')
|
||||||
|
})
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
// middleware.js
|
||||||
|
{
|
||||||
|
input: 'src/middleware/index.ts',
|
||||||
|
output: {
|
||||||
|
file: 'middleware.js',
|
||||||
|
format: 'cjs',
|
||||||
|
paths,
|
||||||
|
sourcemap: true
|
||||||
|
},
|
||||||
|
external,
|
||||||
|
plugins: [
|
||||||
|
typescript({
|
||||||
|
typescript: require('typescript')
|
||||||
|
})
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
// runtime.js
|
// runtime.js
|
||||||
{
|
{
|
||||||
input: 'src/runtime/index.ts',
|
input: 'src/runtime/index.ts',
|
||||||
output: {
|
output: {
|
||||||
file: 'runtime.js',
|
file: 'runtime.js',
|
||||||
format: 'es'
|
format: 'es',
|
||||||
|
paths,
|
||||||
|
sourcemap: true
|
||||||
},
|
},
|
||||||
|
external,
|
||||||
|
plugins: [
|
||||||
|
typescript({
|
||||||
|
typescript: require('typescript')
|
||||||
|
})
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
// webpack/config.js
|
||||||
|
{
|
||||||
|
input: 'src/webpack/index.ts',
|
||||||
|
output: {
|
||||||
|
file: 'webpack/config.js',
|
||||||
|
format: 'cjs',
|
||||||
|
paths,
|
||||||
|
sourcemap: true
|
||||||
|
},
|
||||||
|
external,
|
||||||
plugins: [
|
plugins: [
|
||||||
typescript({
|
typescript({
|
||||||
typescript: require('typescript')
|
typescript: require('typescript')
|
||||||
|
|||||||
1
runtime.js.map
Normal file
1
runtime.js.map
Normal file
File diff suppressed because one or more lines are too long
1
runtime/README.md
Normal file
1
runtime/README.md
Normal file
@@ -0,0 +1 @@
|
|||||||
|
This directory exists for legacy reasons and should be deleted before releasing version 1.
|
||||||
@@ -1,12 +1,11 @@
|
|||||||
#!/usr/bin/env node
|
import { build, export as exporter } from 'sapper/core.js';
|
||||||
|
import { dest, dev, entry, src } from '../config';
|
||||||
const build = require('../lib/build.js');
|
|
||||||
|
|
||||||
const cmd = process.argv[2];
|
const cmd = process.argv[2];
|
||||||
const start = Date.now();
|
const start = Date.now();
|
||||||
|
|
||||||
if (cmd === 'build') {
|
if (cmd === 'build') {
|
||||||
build()
|
build({ dest, dev, entry, src })
|
||||||
.then(() => {
|
.then(() => {
|
||||||
const elapsed = Date.now() - start;
|
const elapsed = Date.now() - start;
|
||||||
console.error(`built in ${elapsed}ms`); // TODO beautify this, e.g. 'built in 4.7 seconds'
|
console.error(`built in ${elapsed}ms`); // TODO beautify this, e.g. 'built in 4.7 seconds'
|
||||||
@@ -17,8 +16,8 @@ if (cmd === 'build') {
|
|||||||
} else if (cmd === 'export') {
|
} else if (cmd === 'export') {
|
||||||
const start = Date.now();
|
const start = Date.now();
|
||||||
|
|
||||||
build()
|
build({ dest, dev, entry, src })
|
||||||
.then(() => require('../lib/utils/export.js')())
|
.then(() => exporter({ src, dest }))
|
||||||
.then(() => {
|
.then(() => {
|
||||||
const elapsed = Date.now() - start;
|
const elapsed = Date.now() - start;
|
||||||
console.error(`extracted in ${elapsed}ms`); // TODO beautify this, e.g. 'built in 4.7 seconds'
|
console.error(`extracted in ${elapsed}ms`); // TODO beautify this, e.g. 'built in 4.7 seconds'
|
||||||
12
src/config.ts
Normal file
12
src/config.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import * as path from 'path';
|
||||||
|
|
||||||
|
export const dev = process.env.NODE_ENV !== 'production';
|
||||||
|
|
||||||
|
export const templates = path.resolve(process.env.SAPPER_TEMPLATES || 'templates');
|
||||||
|
export const src = path.resolve(process.env.SAPPER_ROUTES || 'routes');
|
||||||
|
export const dest = path.resolve(process.env.SAPPER_DEST || '.sapper');
|
||||||
|
|
||||||
|
export const entry = {
|
||||||
|
client: path.resolve(templates, '.main.rendered.js'),
|
||||||
|
server: path.resolve(dest, 'server-entry.js')
|
||||||
|
};
|
||||||
62
src/core/build.ts
Normal file
62
src/core/build.ts
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
import * as fs from 'fs';
|
||||||
|
import * as path from 'path';
|
||||||
|
import mkdirp from 'mkdirp';
|
||||||
|
import rimraf from 'rimraf';
|
||||||
|
import create_compilers from './create_compilers.js';
|
||||||
|
import create_app from './create_app.js';
|
||||||
|
import create_assets from './create_assets.js';
|
||||||
|
|
||||||
|
export default function build({
|
||||||
|
src,
|
||||||
|
dest,
|
||||||
|
dev,
|
||||||
|
entry
|
||||||
|
}: {
|
||||||
|
src: string;
|
||||||
|
dest: string;
|
||||||
|
dev: boolean;
|
||||||
|
entry: { client: string, server: string }
|
||||||
|
}) {
|
||||||
|
mkdirp.sync(dest);
|
||||||
|
rimraf.sync(path.join(dest, '**/*'));
|
||||||
|
|
||||||
|
// create main.js and server-routes.js
|
||||||
|
create_app({ dev, entry, src });
|
||||||
|
|
||||||
|
return new Promise((fulfil, reject) => {
|
||||||
|
function handleErrors(err, stats) {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stats.hasErrors()) {
|
||||||
|
console.error(stats.toString({ colors: true }));
|
||||||
|
reject(new Error(`Encountered errors while building app`));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const { client, server } = create_compilers();
|
||||||
|
|
||||||
|
client.run((err, client_stats) => {
|
||||||
|
handleErrors(err, client_stats);
|
||||||
|
const client_info = client_stats.toJson();
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(dest, 'stats.client.json'),
|
||||||
|
JSON.stringify(client_info, null, ' ')
|
||||||
|
);
|
||||||
|
|
||||||
|
server.run((err, server_stats) => {
|
||||||
|
handleErrors(err, server_stats);
|
||||||
|
const server_info = server_stats.toJson();
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(dest, 'stats.server.json'),
|
||||||
|
JSON.stringify(server_info, null, ' ')
|
||||||
|
);
|
||||||
|
|
||||||
|
create_assets({ src, dest, dev, client_info, server_info });
|
||||||
|
fulfil();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
90
src/core/create_app.ts
Normal file
90
src/core/create_app.ts
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
import * as fs from 'fs';
|
||||||
|
import * as path from 'path';
|
||||||
|
import create_routes from './create_routes';
|
||||||
|
|
||||||
|
function posixify(file: string) {
|
||||||
|
return file.replace(/[/\\]/g, '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
function fudge_mtime(file: string) {
|
||||||
|
// need to fudge the mtime so that webpack doesn't go doolally
|
||||||
|
const { atime, mtime } = fs.statSync(file);
|
||||||
|
fs.utimesSync(
|
||||||
|
file,
|
||||||
|
new Date(atime.getTime() - 999999),
|
||||||
|
new Date(mtime.getTime() - 999999)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function create_app({
|
||||||
|
src,
|
||||||
|
dev,
|
||||||
|
entry
|
||||||
|
}: {
|
||||||
|
src: string;
|
||||||
|
dev: boolean;
|
||||||
|
entry: { client: string; server: string };
|
||||||
|
}) {
|
||||||
|
const routes = create_routes({ src });
|
||||||
|
|
||||||
|
function create_client_main() {
|
||||||
|
const code = `[${routes
|
||||||
|
.filter(route => route.type === 'page')
|
||||||
|
.map(route => {
|
||||||
|
const params =
|
||||||
|
route.dynamic.length === 0
|
||||||
|
? '{}'
|
||||||
|
: `{ ${route.dynamic
|
||||||
|
.map((part, i) => `${part}: match[${i + 1}]`)
|
||||||
|
.join(', ')} }`;
|
||||||
|
|
||||||
|
const file = posixify(`${src}/${route.file}`);
|
||||||
|
return `{ pattern: ${
|
||||||
|
route.pattern
|
||||||
|
}, params: match => (${params}), load: () => import(/* webpackChunkName: "${
|
||||||
|
route.id
|
||||||
|
}" */ '${file}') }`;
|
||||||
|
})
|
||||||
|
.join(', ')}]`;
|
||||||
|
|
||||||
|
let main = fs
|
||||||
|
.readFileSync('templates/main.js', 'utf-8')
|
||||||
|
.replace(
|
||||||
|
/__app__/g,
|
||||||
|
posixify(path.resolve(__dirname, '../../runtime/app.js'))
|
||||||
|
)
|
||||||
|
.replace(/__routes__/g, code)
|
||||||
|
.replace(/__dev__/g, String(dev));
|
||||||
|
|
||||||
|
if (dev) {
|
||||||
|
const hmr_client = posixify(
|
||||||
|
require.resolve(`webpack-hot-middleware/client`)
|
||||||
|
);
|
||||||
|
main += `\n\nimport('${hmr_client}?path=/__webpack_hmr&timeout=20000'); if (module.hot) module.hot.accept();`;
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.writeFileSync(entry.client, main);
|
||||||
|
fudge_mtime(entry.client);
|
||||||
|
}
|
||||||
|
|
||||||
|
function create_server_routes() {
|
||||||
|
const imports = routes
|
||||||
|
.map(route => {
|
||||||
|
const file = posixify(`${src}/${route.file}`);
|
||||||
|
return route.type === 'page'
|
||||||
|
? `import ${route.id} from '${file}';`
|
||||||
|
: `import * as ${route.id} from '${file}';`;
|
||||||
|
})
|
||||||
|
.join('\n');
|
||||||
|
|
||||||
|
const exports = `export { ${routes.map(route => route.id)} };`;
|
||||||
|
|
||||||
|
fs.writeFileSync(entry.server, `${imports}\n\n${exports}`);
|
||||||
|
fudge_mtime(entry.server);
|
||||||
|
}
|
||||||
|
|
||||||
|
create_client_main();
|
||||||
|
create_server_routes();
|
||||||
|
}
|
||||||
|
|
||||||
|
export default create_app;
|
||||||
@@ -1,23 +1,26 @@
|
|||||||
const fs = require('fs');
|
import * as fs from 'fs';
|
||||||
const path = require('path');
|
import * as path from 'path';
|
||||||
const glob = require('glob');
|
import glob from 'glob';
|
||||||
const templates = require('../templates.js');
|
import { create_templates, render } from './templates';
|
||||||
const route_manager = require('../route_manager.js');
|
import create_routes from './create_routes';
|
||||||
const { dest, dev } = require('../config.js');
|
|
||||||
|
|
||||||
function ensure_array(thing) {
|
function ensure_array(thing) {
|
||||||
return Array.isArray(thing) ? thing : [thing]; // omg webpack what the HELL are you doing
|
return Array.isArray(thing) ? thing : [thing]; // omg webpack what the HELL are you doing
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = function generate_asset_cache(clientInfo, serverInfo) {
|
export default function create_assets({ src, dest, dev, client_info, server_info }) {
|
||||||
const main_file = `/client/${ensure_array(clientInfo.assetsByChunkName.main)[0]}`;
|
create_templates(); // TODO refactor this...
|
||||||
|
|
||||||
const chunk_files = clientInfo.assets.map(chunk => `/client/${chunk.name}`);
|
const main_file = `/client/${ensure_array(client_info.assetsByChunkName.main)[0]}`;
|
||||||
|
|
||||||
const service_worker = generate_service_worker(chunk_files);
|
const chunk_files = client_info.assets.map(chunk => `/client/${chunk.name}`);
|
||||||
|
|
||||||
|
const service_worker = generate_service_worker({ chunk_files, src });
|
||||||
const index = generate_index(main_file);
|
const index = generate_index(main_file);
|
||||||
|
|
||||||
if (dev) {
|
const routes = create_routes({ src });
|
||||||
|
|
||||||
|
if (dev) { // TODO move this into calling code
|
||||||
fs.writeFileSync(path.join(dest, 'service-worker.js'), service_worker);
|
fs.writeFileSync(path.join(dest, 'service-worker.js'), service_worker);
|
||||||
fs.writeFileSync(path.join(dest, 'index.html'), index);
|
fs.writeFileSync(path.join(dest, 'index.html'), index);
|
||||||
}
|
}
|
||||||
@@ -33,8 +36,9 @@ module.exports = function generate_asset_cache(clientInfo, serverInfo) {
|
|||||||
return lookup;
|
return lookup;
|
||||||
}, {}),
|
}, {}),
|
||||||
|
|
||||||
routes: route_manager.routes.reduce((lookup, route) => {
|
// TODO confusing that `routes` refers to an array *and* a lookup
|
||||||
lookup[route.id] = `/client/${ensure_array(clientInfo.assetsByChunkName[route.id])[0]}`;
|
routes: routes.reduce((lookup, route) => {
|
||||||
|
lookup[route.id] = `/client/${ensure_array(client_info.assetsByChunkName[route.id])[0]}`;
|
||||||
return lookup;
|
return lookup;
|
||||||
}, {}),
|
}, {}),
|
||||||
|
|
||||||
@@ -43,18 +47,20 @@ module.exports = function generate_asset_cache(clientInfo, serverInfo) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
server: {
|
server: {
|
||||||
entry: path.resolve(dest, 'server', serverInfo.assetsByChunkName.main)
|
entry: path.resolve(dest, 'server', server_info.assetsByChunkName.main)
|
||||||
},
|
},
|
||||||
|
|
||||||
service_worker
|
service_worker
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
function generate_service_worker(chunk_files) {
|
function generate_service_worker({ chunk_files, src }) {
|
||||||
const assets = glob.sync('**', { cwd: 'assets', nodir: true });
|
const assets = glob.sync('**', { cwd: 'assets', nodir: true });
|
||||||
|
|
||||||
|
const routes = create_routes({ src });
|
||||||
|
|
||||||
const route_code = `[${
|
const route_code = `[${
|
||||||
route_manager.routes
|
routes
|
||||||
.filter(route => route.type === 'page')
|
.filter(route => route.type === 'page')
|
||||||
.map(route => `{ pattern: ${route.pattern} }`)
|
.map(route => `{ pattern: ${route.pattern} }`)
|
||||||
.join(', ')
|
.join(', ')
|
||||||
@@ -68,7 +74,7 @@ function generate_service_worker(chunk_files) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function generate_index(main_file) {
|
function generate_index(main_file) {
|
||||||
return templates.render(200, {
|
return render(200, {
|
||||||
styles: '',
|
styles: '',
|
||||||
head: '',
|
head: '',
|
||||||
html: '<noscript>Please enable JavaScript!</noscript>',
|
html: '<noscript>Please enable JavaScript!</noscript>',
|
||||||
16
src/core/create_compilers.ts
Normal file
16
src/core/create_compilers.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import * as path from 'path';
|
||||||
|
import relative from 'require-relative';
|
||||||
|
|
||||||
|
export default function create_compilers() {
|
||||||
|
const webpack = relative('webpack', process.cwd());
|
||||||
|
|
||||||
|
return {
|
||||||
|
client: webpack(
|
||||||
|
require(path.resolve('webpack.client.config.js'))
|
||||||
|
),
|
||||||
|
|
||||||
|
server: webpack(
|
||||||
|
require(path.resolve('webpack.server.config.js'))
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
const path = require('path');
|
import * as path from 'path';
|
||||||
|
import glob from 'glob';
|
||||||
|
|
||||||
module.exports = function create_matchers(files) {
|
export default function create_routes({ src, files = glob.sync('**/*.+(html|js|mjs)', { cwd: src }) }) {
|
||||||
const routes = files
|
const routes = files
|
||||||
.map(file => {
|
.map(file => {
|
||||||
if (/(^|\/|\\)_/.test(file)) return;
|
if (/(^|\/|\\)_/.test(file)) return;
|
||||||
@@ -87,4 +88,4 @@ module.exports = function create_matchers(files) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return routes;
|
return routes;
|
||||||
};
|
}
|
||||||
@@ -1,29 +1,32 @@
|
|||||||
const path = require('path');
|
import * as path from 'path';
|
||||||
const sander = require('sander');
|
import * as sander from 'sander';
|
||||||
const app = require('express')();
|
import express from 'express';
|
||||||
const cheerio = require('cheerio');
|
import cheerio from 'cheerio';
|
||||||
const fetch = require('node-fetch');
|
import fetch from 'node-fetch';
|
||||||
const URL = require('url-parse');
|
import URL from 'url-parse';
|
||||||
const generate_asset_cache = require('./generate_asset_cache.js');
|
import create_assets from './create_assets.js';
|
||||||
const sapper = require('../index.js');
|
// import middleware from '../middleware/index.js';
|
||||||
|
|
||||||
const { PORT = 3000, OUTPUT_DIR = 'dist' } = process.env;
|
const { PORT = 3000, OUTPUT_DIR = 'dist' } = process.env;
|
||||||
const { dest } = require('../config.js');
|
|
||||||
|
|
||||||
const origin = `http://localhost:${PORT}`;
|
const origin = `http://localhost:${PORT}`;
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
|
||||||
function read_json(file) {
|
function read_json(file) {
|
||||||
return JSON.parse(sander.readFileSync(file, { encoding: 'utf-8' }));
|
return JSON.parse(sander.readFileSync(file, { encoding: 'utf-8' }));
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = function() {
|
export default function exporter({ src, dest }) { // TODO dest is a terrible name in this context
|
||||||
// Prep output directory
|
// Prep output directory
|
||||||
sander.rimrafSync(OUTPUT_DIR);
|
sander.rimrafSync(OUTPUT_DIR);
|
||||||
|
|
||||||
const { service_worker } = generate_asset_cache(
|
const { service_worker } = create_assets({
|
||||||
read_json(path.join(dest, 'stats.client.json')),
|
src, dest,
|
||||||
read_json(path.join(dest, 'stats.server.json'))
|
dev: false,
|
||||||
);
|
client_info: read_json(path.join(dest, 'stats.client.json')),
|
||||||
|
server_info: read_json(path.join(dest, 'stats.server.json'))
|
||||||
|
});
|
||||||
|
|
||||||
sander.copydirSync('assets').to(OUTPUT_DIR);
|
sander.copydirSync('assets').to(OUTPUT_DIR);
|
||||||
sander.copydirSync(dest, 'client').to(OUTPUT_DIR, 'client');
|
sander.copydirSync(dest, 'client').to(OUTPUT_DIR, 'client');
|
||||||
@@ -60,7 +63,7 @@ module.exports = function() {
|
|||||||
return fetch(url, opts);
|
return fetch(url, opts);
|
||||||
};
|
};
|
||||||
|
|
||||||
app.use(sapper());
|
app.use(require('./middleware')()); // TODO this is filthy
|
||||||
const server = app.listen(PORT);
|
const server = app.listen(PORT);
|
||||||
|
|
||||||
const seen = new Set();
|
const seen = new Set();
|
||||||
@@ -95,4 +98,4 @@ module.exports = function() {
|
|||||||
|
|
||||||
return handle(new URL(origin)) // TODO all static routes
|
return handle(new URL(origin)) // TODO all static routes
|
||||||
.then(() => server.close());
|
.then(() => server.close());
|
||||||
};
|
}
|
||||||
11
src/core/index.ts
Normal file
11
src/core/index.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { create_templates, render, stream } from './templates'; // TODO templates is an anomaly... fix post-#91
|
||||||
|
|
||||||
|
export { default as build } from './build';
|
||||||
|
export { default as export } from './export.js';
|
||||||
|
|
||||||
|
export { default as create_app } from './create_app';
|
||||||
|
export { default as create_assets } from './create_assets';
|
||||||
|
export { default as create_compilers } from './create_compilers';
|
||||||
|
export { default as create_routes } from './create_routes';
|
||||||
|
|
||||||
|
export const templates = { create_templates, render, stream };
|
||||||
@@ -1,9 +1,8 @@
|
|||||||
const fs = require('fs');
|
import * as fs from 'fs';
|
||||||
const glob = require('glob');
|
import glob from 'glob';
|
||||||
const chalk = require('chalk');
|
import chalk from 'chalk';
|
||||||
const framer = require('code-frame');
|
import framer from 'code-frame';
|
||||||
const { locate } = require('locate-character');
|
import { locate } from 'locate-character';
|
||||||
const { dev } = require('./config.js');
|
|
||||||
|
|
||||||
let templates;
|
let templates;
|
||||||
|
|
||||||
@@ -16,7 +15,7 @@ function error(e) {
|
|||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
function create_templates() {
|
export function create_templates() {
|
||||||
templates = glob.sync('*.html', { cwd: 'templates' })
|
templates = glob.sync('*.html', { cwd: 'templates' })
|
||||||
.map(file => {
|
.map(file => {
|
||||||
const template = fs.readFileSync(`templates/${file}`, 'utf-8');
|
const template = fs.readFileSync(`templates/${file}`, 'utf-8');
|
||||||
@@ -97,31 +96,20 @@ function create_templates() {
|
|||||||
};
|
};
|
||||||
})
|
})
|
||||||
.sort((a, b) => b.specificity - a.specificity);
|
.sort((a, b) => b.specificity - a.specificity);
|
||||||
|
|
||||||
|
return templates;
|
||||||
}
|
}
|
||||||
|
|
||||||
create_templates();
|
export function render(status, data) {
|
||||||
|
|
||||||
if (dev) {
|
|
||||||
const watcher = require('chokidar').watch('templates/**.html', {
|
|
||||||
ignoreInitial: true,
|
|
||||||
persistent: false
|
|
||||||
});
|
|
||||||
|
|
||||||
watcher.on('add', create_templates);
|
|
||||||
watcher.on('change', create_templates);
|
|
||||||
watcher.on('unlink', create_templates);
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.render = (status, data) => {
|
|
||||||
const template = templates.find(template => template.test(status));
|
const template = templates.find(template => template.test(status));
|
||||||
if (template) return template.render(data);
|
if (template) return template.render(data);
|
||||||
|
|
||||||
return `Missing template for status code ${status}`;
|
return `Missing template for status code ${status}`;
|
||||||
};
|
}
|
||||||
|
|
||||||
exports.stream = (res, status, data) => {
|
export function stream(res, status, data) {
|
||||||
const template = templates.find(template => template.test(status));
|
const template = templates.find(template => template.test(status));
|
||||||
if (template) return template.stream(res, data);
|
if (template) return template.stream(res, data);
|
||||||
|
|
||||||
return `Missing template for status code ${status}`;
|
return `Missing template for status code ${status}`;
|
||||||
};
|
}
|
||||||
@@ -1,9 +1,8 @@
|
|||||||
const fs = require('fs');
|
import * as fs from 'fs';
|
||||||
const path = require('path');
|
import * as path from 'path';
|
||||||
const chalk = require('chalk');
|
import chalk from 'chalk';
|
||||||
const compilers = require('./compilers.js');
|
import { create_app, create_assets, create_routes, templates } from 'sapper/core.js';
|
||||||
const generate_asset_cache = require('./generate_asset_cache.js');
|
import { dest } from '../config.js';
|
||||||
const { dest } = require('../config.js');
|
|
||||||
|
|
||||||
function deferred() {
|
function deferred() {
|
||||||
const d = {};
|
const d = {};
|
||||||
@@ -16,7 +15,7 @@ function deferred() {
|
|||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = function create_watcher() {
|
export default function create_watcher({ compilers, dev, entry, src, onroutes }) {
|
||||||
const deferreds = {
|
const deferreds = {
|
||||||
client: deferred(),
|
client: deferred(),
|
||||||
server: deferred()
|
server: deferred()
|
||||||
@@ -32,10 +31,11 @@ module.exports = function create_watcher() {
|
|||||||
const server_info = server_stats.toJson();
|
const server_info = server_stats.toJson();
|
||||||
fs.writeFileSync(path.join(dest, 'stats.server.json'), JSON.stringify(server_info, null, ' '));
|
fs.writeFileSync(path.join(dest, 'stats.server.json'), JSON.stringify(server_info, null, ' '));
|
||||||
|
|
||||||
return generate_asset_cache(
|
return create_assets({
|
||||||
client_stats.toJson(),
|
src, dest, dev,
|
||||||
server_stats.toJson()
|
client_info: client_stats.toJson(),
|
||||||
);
|
server_info: server_stats.toJson()
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function watch_compiler(type) {
|
function watch_compiler(type) {
|
||||||
@@ -60,6 +60,34 @@ module.exports = function create_watcher() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const chokidar = require('chokidar');
|
||||||
|
|
||||||
|
function watch_files(pattern, callback) {
|
||||||
|
const watcher = chokidar.watch(pattern, {
|
||||||
|
persistent: false
|
||||||
|
});
|
||||||
|
|
||||||
|
watcher.on('add', callback);
|
||||||
|
watcher.on('change', callback);
|
||||||
|
watcher.on('unlink', callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
watch_files('routes/**/*.+(html|js|mjs)', () => {
|
||||||
|
const routes = create_routes({ src });
|
||||||
|
onroutes(routes);
|
||||||
|
|
||||||
|
create_app({ dev, entry, src }); // TODO this calls `create_routes` again, we should pass `routes` to `create_app` instead
|
||||||
|
});
|
||||||
|
|
||||||
|
watch_files('templates/main.js', () => {
|
||||||
|
create_app({ dev, entry, src });
|
||||||
|
});
|
||||||
|
|
||||||
|
watch_files('templates/**.html', () => {
|
||||||
|
templates.create_templates();
|
||||||
|
// TODO reload current page?
|
||||||
|
});
|
||||||
|
|
||||||
const watcher = {
|
const watcher = {
|
||||||
ready: invalidate(),
|
ready: invalidate(),
|
||||||
client: watch_compiler('client'),
|
client: watch_compiler('client'),
|
||||||
@@ -72,4 +100,4 @@ module.exports = function create_watcher() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return watcher;
|
return watcher;
|
||||||
};
|
}
|
||||||
@@ -1,19 +1,28 @@
|
|||||||
const fs = require('fs');
|
import * as fs from 'fs';
|
||||||
const path = require('path');
|
import * as path from 'path';
|
||||||
const serialize = require('serialize-javascript');
|
import mkdirp from 'mkdirp';
|
||||||
const route_manager = require('./route_manager.js');
|
import rimraf from 'rimraf';
|
||||||
const templates = require('./templates.js');
|
import serialize from 'serialize-javascript';
|
||||||
const create_app = require('./utils/create_app.js');
|
import escape_html from 'escape-html';
|
||||||
const create_watcher = require('./utils/create_watcher.js');
|
import { create_routes, templates, create_compilers, create_assets } from 'sapper/core.js';
|
||||||
const compilers = require('./utils/compilers.js');
|
import create_watcher from './create_watcher';
|
||||||
const generate_asset_cache = require('./utils/generate_asset_cache.js');
|
import { dest, dev, entry, src } from '../config';
|
||||||
const escape_html = require('escape-html');
|
|
||||||
const { dest, dev } = require('./config.js');
|
|
||||||
|
|
||||||
function connect_dev() {
|
function connect_dev() {
|
||||||
create_app();
|
mkdirp.sync(dest);
|
||||||
|
rimraf.sync(path.join(dest, '**/*'));
|
||||||
|
|
||||||
const watcher = create_watcher();
|
const compilers = create_compilers();
|
||||||
|
|
||||||
|
let routes;
|
||||||
|
|
||||||
|
const watcher = create_watcher({
|
||||||
|
dev, entry, src,
|
||||||
|
compilers,
|
||||||
|
onroutes: _ => {
|
||||||
|
routes = _;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
let asset_cache;
|
let asset_cache;
|
||||||
|
|
||||||
@@ -54,7 +63,7 @@ function connect_dev() {
|
|||||||
fn: pathname => asset_cache.client.chunks[pathname]
|
fn: pathname => asset_cache.client.chunks[pathname]
|
||||||
}),
|
}),
|
||||||
|
|
||||||
get_route_handler(() => asset_cache),
|
get_route_handler(() => asset_cache, () => routes),
|
||||||
|
|
||||||
get_not_found_handler(() => asset_cache)
|
get_not_found_handler(() => asset_cache)
|
||||||
]);
|
]);
|
||||||
@@ -68,10 +77,14 @@ function connect_dev() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function connect_prod() {
|
function connect_prod() {
|
||||||
const asset_cache = generate_asset_cache(
|
const asset_cache = create_assets({
|
||||||
read_json(path.join(dest, 'stats.client.json')),
|
src, dest,
|
||||||
read_json(path.join(dest, 'stats.server.json'))
|
dev: false,
|
||||||
);
|
client_info: read_json(path.join(dest, 'stats.client.json')),
|
||||||
|
server_info: read_json(path.join(dest, 'stats.server.json'))
|
||||||
|
});
|
||||||
|
|
||||||
|
const routes = create_routes({ src }); // TODO rename update
|
||||||
|
|
||||||
const middleware = compose_handlers([
|
const middleware = compose_handlers([
|
||||||
set_req_pathname,
|
set_req_pathname,
|
||||||
@@ -97,7 +110,7 @@ function connect_prod() {
|
|||||||
fn: pathname => asset_cache.client.chunks[pathname]
|
fn: pathname => asset_cache.client.chunks[pathname]
|
||||||
}),
|
}),
|
||||||
|
|
||||||
get_route_handler(() => asset_cache),
|
get_route_handler(() => asset_cache, () => routes),
|
||||||
|
|
||||||
get_not_found_handler(() => asset_cache)
|
get_not_found_handler(() => asset_cache)
|
||||||
]);
|
]);
|
||||||
@@ -109,7 +122,7 @@ function connect_prod() {
|
|||||||
return middleware;
|
return middleware;
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = dev ? connect_dev : connect_prod;
|
export default dev ? connect_dev : connect_prod;
|
||||||
|
|
||||||
function set_req_pathname(req, res, next) {
|
function set_req_pathname(req, res, next) {
|
||||||
req.pathname = req.url.replace(/\?.+/, '');
|
req.pathname = req.url.replace(/\?.+/, '');
|
||||||
@@ -129,7 +142,7 @@ function get_asset_handler(opts) {
|
|||||||
|
|
||||||
const resolved = Promise.resolve();
|
const resolved = Promise.resolve();
|
||||||
|
|
||||||
function get_route_handler(fn) {
|
function get_route_handler(get_assets, get_routes) {
|
||||||
function handle_route(route, req, res, next, { client, server }) {
|
function handle_route(route, req, res, next, { client, server }) {
|
||||||
req.params = route.exec(req.pathname);
|
req.params = route.exec(req.pathname);
|
||||||
|
|
||||||
@@ -201,8 +214,9 @@ function get_route_handler(fn) {
|
|||||||
|
|
||||||
resolved
|
resolved
|
||||||
.then(() => {
|
.then(() => {
|
||||||
for (const route of route_manager.routes) {
|
const routes = get_routes();
|
||||||
if (route.test(url)) return handle_route(route, req, res, next, fn());
|
for (const route of routes) {
|
||||||
|
if (route.test(url)) return handle_route(route, req, res, next, get_assets());
|
||||||
}
|
}
|
||||||
|
|
||||||
// no matching route — 404
|
// no matching route — 404
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
const { dest, dev, entry } = require('../lib/config.js');
|
import { dest, dev, entry } from '../config';
|
||||||
|
|
||||||
module.exports = {
|
export default {
|
||||||
dev,
|
dev,
|
||||||
|
|
||||||
client: {
|
client: {
|
||||||
@@ -1,11 +1,10 @@
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
|
const Nightmare = require('nightmare');
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const serve = require('serve-static');
|
const serve = require('serve-static');
|
||||||
const Nightmare = require('nightmare');
|
|
||||||
const getPort = require('get-port');
|
|
||||||
const fetch = require('node-fetch');
|
|
||||||
const walkSync = require('walk-sync');
|
const walkSync = require('walk-sync');
|
||||||
|
const fetch = require('node-fetch');
|
||||||
|
|
||||||
run('production');
|
run('production');
|
||||||
run('development');
|
run('development');
|
||||||
@@ -82,16 +81,18 @@ function run(env) {
|
|||||||
let sapper;
|
let sapper;
|
||||||
|
|
||||||
if (env === 'production') {
|
if (env === 'production') {
|
||||||
const cli = path.resolve(__dirname, '../../cli/index.js');
|
const cli = path.resolve(__dirname, '../../cli.js');
|
||||||
exec_promise = exec(`node ${cli} export`);
|
exec_promise = exec(`node ${cli} export`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return exec_promise.then(() => {
|
return exec_promise.then(() => {
|
||||||
const resolved = require.resolve('../..');
|
const resolved = require.resolve('../../middleware.js');
|
||||||
delete require.cache[resolved];
|
delete require.cache[resolved];
|
||||||
|
delete require.cache[require.resolve('../../core.js')]; // TODO remove this
|
||||||
|
|
||||||
sapper = require(resolved);
|
sapper = require(resolved);
|
||||||
|
|
||||||
return getPort();
|
return require('get-port')();
|
||||||
}).then(port => {
|
}).then(port => {
|
||||||
PORT = port;
|
PORT = port;
|
||||||
base = `http://localhost:${PORT}`;
|
base = `http://localhost:${PORT}`;
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
const path = require('path');
|
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
|
const { create_routes } = require('../../core.js');
|
||||||
const create_routes = require('../../lib/utils/create_routes.js');
|
|
||||||
|
|
||||||
describe('create_routes', () => {
|
describe('create_routes', () => {
|
||||||
it('sorts routes correctly', () => {
|
it('sorts routes correctly', () => {
|
||||||
const routes = create_routes(['index.html', 'about.html', '[wildcard].html', 'post/foo.html', 'post/[id].html', 'post/bar.html']);
|
const routes = create_routes({
|
||||||
|
files: ['index.html', 'about.html', '[wildcard].html', 'post/foo.html', 'post/[id].html', 'post/bar.html']
|
||||||
|
});
|
||||||
|
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
routes.map(r => r.file),
|
routes.map(r => r.file),
|
||||||
@@ -21,7 +21,9 @@ describe('create_routes', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('generates params', () => {
|
it('generates params', () => {
|
||||||
const routes = create_routes(['index.html', 'about.html', '[wildcard].html', 'post/[id].html']);
|
const routes = create_routes({
|
||||||
|
files: ['index.html', 'about.html', '[wildcard].html', 'post/[id].html']
|
||||||
|
});
|
||||||
|
|
||||||
let file;
|
let file;
|
||||||
let params;
|
let params;
|
||||||
@@ -40,7 +42,9 @@ describe('create_routes', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('ignores files and directories with leading underscores', () => {
|
it('ignores files and directories with leading underscores', () => {
|
||||||
const routes = create_routes(['index.html', '_foo.html', 'a/_b/c/d.html', 'e/f/g/h.html', 'i/_j.html']);
|
const routes = create_routes({
|
||||||
|
files: ['index.html', '_foo.html', 'a/_b/c/d.html', 'e/f/g/h.html', 'i/_j.html']
|
||||||
|
});
|
||||||
|
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
routes.map(r => r.file),
|
routes.map(r => r.file),
|
||||||
@@ -52,8 +56,12 @@ describe('create_routes', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('matches /foo/:bar before /:baz/qux', () => {
|
it('matches /foo/:bar before /:baz/qux', () => {
|
||||||
const a = create_routes(['foo/[bar].html', '[baz]/qux.html']);
|
const a = create_routes({
|
||||||
const b = create_routes(['[baz]/qux.html', 'foo/[bar].html']);
|
files: ['foo/[bar].html', '[baz]/qux.html']
|
||||||
|
});
|
||||||
|
const b = create_routes({
|
||||||
|
files: ['[baz]/qux.html', 'foo/[bar].html']
|
||||||
|
});
|
||||||
|
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
a.map(r => r.file),
|
a.map(r => r.file),
|
||||||
@@ -68,16 +76,22 @@ describe('create_routes', () => {
|
|||||||
|
|
||||||
it('fails if routes are indistinguishable', () => {
|
it('fails if routes are indistinguishable', () => {
|
||||||
assert.throws(() => {
|
assert.throws(() => {
|
||||||
create_routes(['[foo].html', '[bar]/index.html']);
|
create_routes({
|
||||||
|
files: ['[foo].html', '[bar]/index.html']
|
||||||
|
});
|
||||||
}, /The \[foo\].html and \[bar\]\/index.html routes clash/);
|
}, /The \[foo\].html and \[bar\]\/index.html routes clash/);
|
||||||
|
|
||||||
assert.throws(() => {
|
assert.throws(() => {
|
||||||
create_routes(['foo.html', 'foo.js']);
|
create_routes({
|
||||||
|
files: ['foo.html', 'foo.js']
|
||||||
|
});
|
||||||
}, /The foo.html and foo.js routes clash/);
|
}, /The foo.html and foo.js routes clash/);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('matches nested routes', () => {
|
it('matches nested routes', () => {
|
||||||
const route = create_routes(['settings/[submenu].html'])[0];
|
const route = create_routes({
|
||||||
|
files: ['settings/[submenu].html']
|
||||||
|
})[0];
|
||||||
|
|
||||||
assert.deepEqual(route.exec('/settings/foo'), {
|
assert.deepEqual(route.exec('/settings/foo'), {
|
||||||
submenu: 'foo'
|
submenu: 'foo'
|
||||||
@@ -89,7 +103,9 @@ describe('create_routes', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('prefers index routes to nested routes', () => {
|
it('prefers index routes to nested routes', () => {
|
||||||
const routes = create_routes(['settings/[submenu].html', 'settings.html']);
|
const routes = create_routes({
|
||||||
|
files: ['settings/[submenu].html', 'settings.html']
|
||||||
|
});
|
||||||
|
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
routes.map(r => r.file),
|
routes.map(r => r.file),
|
||||||
@@ -98,7 +114,9 @@ describe('create_routes', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('matches deeply nested routes', () => {
|
it('matches deeply nested routes', () => {
|
||||||
const route = create_routes(['settings/[a]/[b]/index.html'])[0];
|
const route = create_routes({
|
||||||
|
files: ['settings/[a]/[b]/index.html']
|
||||||
|
})[0];
|
||||||
|
|
||||||
assert.deepEqual(route.exec('/settings/foo/bar'), {
|
assert.deepEqual(route.exec('/settings/foo/bar'), {
|
||||||
a: 'foo',
|
a: 'foo',
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
import 'webpack-hot-middleware/client?path=/__webpack_hmr&timeout=20000';
|
|
||||||
Reference in New Issue
Block a user