diff --git a/src/api/build.ts b/src/api/build.ts new file mode 100644 index 0000000..6b19040 --- /dev/null +++ b/src/api/build.ts @@ -0,0 +1,110 @@ +import * as fs from 'fs'; +import * as path from 'path'; +import mkdirp from 'mkdirp'; +import rimraf from 'rimraf'; +import { EventEmitter } from 'events'; +import { minify_html } from './utils/minify_html'; +import { create_compilers, create_main_manifests, create_routes, create_serviceworker_manifest } from '../core' +import { locations } from '../config'; + +export default function build(opts: {}) { + const emitter = new EventEmitter(); + + execute(emitter, opts).then( + () => { + emitter.emit('done', {}); // TODO do we need to pass back any info? + }, + error => { + emitter.emit('error', { + error + }); + } + ); + + return emitter; +} + +async function execute(emitter: EventEmitter, { + dest = 'build', + app = 'app', + webpack = 'webpack', + routes = 'routes' +} = {}) { + mkdirp.sync(dest); + rimraf.sync(path.join(dest, '**/*')); + + // minify app/template.html + // TODO compile this to a function? could be quicker than str.replace(...).replace(...).replace(...) + const template = fs.readFileSync(`${app}/template.html`, 'utf-8'); + + // remove this in a future version + if (template.indexOf('%sapper.base%') === -1) { + const error = new Error(`As of Sapper v0.10, your template.html file must include %sapper.base% in the `); + error.code = `missing-sapper-base`; + throw error; + } + + fs.writeFileSync(`${dest}/template.html`, minify_html(template)); + + const route_objects = create_routes(); + + // create app/manifest/client.js and app/manifest/server.js + create_main_manifests({ routes: route_objects }); + + const { client, server, serviceworker } = create_compilers({ webpack }); + + const client_stats = await compile(client); + emitter.emit('build', { + type: 'client', + // TODO duration/warnings + webpack_stats: client_stats + }); + + fs.writeFileSync(path.join(dest, 'client_info.json'), JSON.stringify({ + assets: client_stats.toJson().assetsByChunkName + })); + + const server_stats = await compile(server); + emitter.emit('build', { + type: 'server', + // TODO duration/warnings + webpack_stats: server_stats + }); + + let serviceworker_stats; + + if (serviceworker) { + create_serviceworker_manifest({ + routes: route_objects, + client_files: client_stats.toJson().assets.map((chunk: { name: string }) => `client/${chunk.name}`) + }); + + serviceworker_stats = await compile(serviceworker); + + emitter.emit('build', { + type: 'serviceworker', + // TODO duration/warnings + webpack_stats: serviceworker_stats + }); + } +} + +function compile(compiler: any) { + return new Promise((fulfil, reject) => { + compiler.run((err: Error, stats: any) => { + if (err) { + reject(err); + process.exit(1); + } + + if (stats.hasErrors()) { + console.error(stats.toString({ colors: true })); + reject(new Error(`Encountered errors while building app`)); + } + + else { + fulfil(stats); + } + }); + }); +} diff --git a/src/api/dev.ts b/src/api/dev.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/api/export.ts b/src/api/export.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/cli/utils/minify_html.ts b/src/api/utils/minify_html.ts similarity index 100% rename from src/cli/utils/minify_html.ts rename to src/api/utils/minify_html.ts diff --git a/src/cli/build.ts b/src/cli/build.ts index 532b1da..332ef32 100644 --- a/src/cli/build.ts +++ b/src/cli/build.ts @@ -1,78 +1,34 @@ -import * as fs from 'fs'; -import * as path from 'path'; +import _build from '../api/build'; import * as colors from 'ansi-colors'; -import mkdirp from 'mkdirp'; -import rimraf from 'rimraf'; -import { minify_html } from './utils/minify_html'; -import { create_compilers, create_main_manifests, create_routes, create_serviceworker_manifest } from '../core' import { locations } from '../config'; -export async function build() { - const output = locations.dest(); - - mkdirp.sync(output); - rimraf.sync(path.join(output, '**/*')); - - // minify app/template.html - // TODO compile this to a function? could be quicker than str.replace(...).replace(...).replace(...) - const template = fs.readFileSync(`${locations.app()}/template.html`, 'utf-8'); - - // remove this in a future version - if (template.indexOf('%sapper.base%') === -1) { - console.log(`${colors.bold.red(`> As of Sapper v0.10, your template.html file must include %sapper.base% in the `)}`); - process.exit(1); - } - - fs.writeFileSync(`${output}/template.html`, minify_html(template)); - - const routes = create_routes(); - - // create app/manifest/client.js and app/manifest/server.js - create_main_manifests({ routes }); - - const { client, server, serviceworker } = create_compilers(); - - const client_stats = await compile(client); - console.log(`${colors.inverse(`\nbuilt client`)}`); - console.log(client_stats.toString({ colors: true })); - fs.writeFileSync(path.join(output, 'client_info.json'), JSON.stringify({ - assets: client_stats.toJson().assetsByChunkName - })); - - const server_stats = await compile(server); - console.log(`${colors.inverse(`\nbuilt server`)}`); - console.log(server_stats.toString({ colors: true })); - - let serviceworker_stats; - - if (serviceworker) { - create_serviceworker_manifest({ - routes, - client_files: client_stats.toJson().assets.map((chunk: { name: string }) => `client/${chunk.name}`) - }); - - serviceworker_stats = await compile(serviceworker); - console.log(`${colors.inverse(`\nbuilt service worker`)}`); - console.log(serviceworker_stats.toString({ colors: true })); - } -} - -function compile(compiler: any) { +export function build() { return new Promise((fulfil, reject) => { - compiler.run((err: Error, stats: any) => { - if (err) { - reject(err); - process.exit(1); - } + try { + console.log('1'); + const emitter = _build({ + dest: locations.dest(), + app: locations.app(), + routes: locations.routes(), + webpack: 'webpack' + }); + console.log('2', emitter); - if (stats.hasErrors()) { - console.error(stats.toString({ colors: true })); - reject(new Error(`Encountered errors while building app`)); - } + emitter.on('build', event => { + console.log(colors.inverse(`\nbuilt ${event.type}`)); + console.log(event.webpack_stats.toString({ colors: true })); + }); - else { - fulfil(stats); - } - }); + emitter.on('error', event => { + reject(event.error); + }); + + emitter.on('done', event => { + fulfil(); + }); + } catch (err) { + console.log(`${colors.bold.red(`> ${err.message}`)}`); + process.exit(1); + } }); -} +} \ No newline at end of file diff --git a/src/cli/export.ts b/src/cli/export.ts index 0f179ba..d90fad0 100644 --- a/src/cli/export.ts +++ b/src/cli/export.ts @@ -7,7 +7,7 @@ import URL from 'url-parse'; import fetch from 'node-fetch'; import * as ports from 'port-authority'; import prettyBytes from 'pretty-bytes'; -import { minify_html } from './utils/minify_html'; +import { minify_html } from '../api/utils/minify_html'; import { locations } from '../config'; export async function exporter(export_dir: string, { basepath = '' }) { diff --git a/src/core/create_compilers.ts b/src/core/create_compilers.ts index bf33802..51a866d 100644 --- a/src/core/create_compilers.ts +++ b/src/core/create_compilers.ts @@ -1,21 +1,21 @@ import * as path from 'path'; import relative from 'require-relative'; -export default function create_compilers() { - const webpack = relative('webpack', process.cwd()); +export default function create_compilers({ webpack }: { webpack: string }) { + const wp = relative('webpack', process.cwd()); - const serviceworker_config = try_require(path.resolve('webpack/service-worker.config.js')); + const serviceworker_config = try_require(path.resolve(`${webpack}/service-worker.config.js`)); return { - client: webpack( - require(path.resolve('webpack/client.config.js')) + client: wp( + require(path.resolve(`${webpack}/client.config.js`)) ), - server: webpack( - require(path.resolve('webpack/server.config.js')) + server: wp( + require(path.resolve(`${webpack}/server.config.js`)) ), - serviceworker: serviceworker_config && webpack(serviceworker_config) + serviceworker: serviceworker_config && wp(serviceworker_config) }; }