UNPKG

6.18 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3const fs = require("fs-extra");
4const chalk_1 = require("chalk");
5const util = require("util");
6const globby = require("globby");
7const path = require("path");
8// @ts-ignore
9const _zip = require("deterministic-zip");
10const webpack = require("webpack");
11const hasha = require("hasha");
12const zip = util.promisify(_zip);
13const _log = (str) => `${chalk_1.default.gray(`[FAB:Compile]`)} ${str
14 .split(/\n\s*/)
15 .join('\n ')}`;
16const log = (str) => console.log(_log(str));
17const error = (str) => console.log(_log(chalk_1.default.red(str)));
18class Compiler {
19 static async compile(input_dir, build_dir, output_file) {
20 const input_path = path.resolve(input_dir);
21 const build_path = path.resolve(build_dir);
22 if (!(await fs.pathExists(input_path))) {
23 error(`${input_path} doesn't exist!`);
24 throw new Error('Missing directory');
25 }
26 log(`Compiling ${chalk_1.default.green(input_path)}`);
27 await fs.emptyDir(build_path);
28 const assets_path = path.join(input_path, '_assets');
29 if (!(await fs.pathExists(assets_path))) {
30 log(`${chalk_1.default.yellow(`No _assets directory detected.`)}
31 This isn't necessarily a problem, but your FAB may be able to be optimised.
32 For more info, visit https://fab-spec.org/eh?no-assets-dir`);
33 }
34 else {
35 log(`Copying fingerprinted _assets:`);
36 await fs.copy(assets_path, path.join(build_path, '_assets'), {
37 filter(src, dest) {
38 if (src.match(/\.\w+$/))
39 log(` ${chalk_1.default.gray(input_dir + '/')}${chalk_1.default.yellow(path.relative(input_path, src))} => ${chalk_1.default.gray(build_dir + '/')}/${chalk_1.default.yellow(path.relative(build_path, dest))}`);
40 return true;
41 }
42 });
43 log(`Done!`);
44 }
45 log(`Copying non-fingerprinted (public) assets:`);
46 const paths = await globby(['**/*', '!_server', '!_assets'], {
47 cwd: input_path
48 });
49 const renames = {};
50 await Promise.all(paths.map(async (filename) => {
51 const full_hash = await hasha.fromFile(path.join(input_path, filename), {
52 algorithm: 'md5'
53 });
54 const hash = full_hash.substring(0, 9);
55 const asset_path = `_assets/_public/${filename.replace(/([^.]*)$/, `${hash}.$1`)}`;
56 renames['/' + filename] = '/' + asset_path;
57 log(` ${chalk_1.default.gray(input_dir + '/')}${chalk_1.default.yellow(filename)} => ${chalk_1.default.gray(build_dir + '/')}${chalk_1.default.yellow(asset_path)}`);
58 await fs.copy(path.join(input_path, filename), path.join(build_path, asset_path));
59 }));
60 log(`Done!`);
61 const server_path = path.join(input_path, '_server', 'index.js');
62 const settings_path = path.join(input_path, '_server', 'production-settings.json');
63 if (!(await fs.pathExists(server_path))) {
64 log(`${chalk_1.default.yellow(`No _server/index.js file detected.`)}
65 Your FAB will only have the simplest of webservers injected.
66 If you want to host a static site, you probably want @fab/static
67 https://fab-spec.org/eh?no-server-index`);
68 await this.webpack(path.resolve(__dirname, 'files/fallback-index.js'), settings_path, build_path, renames);
69 }
70 else {
71 log(`Injecting FAB wrapper and compiling ${chalk_1.default.green(server_path)}`);
72 await this.webpack(server_path, settings_path, build_path, renames);
73 }
74 const bundle_output = path.join(build_path, 'server.js');
75 const stats1 = await fs.stat(bundle_output);
76 log(` ${path.relative(process.cwd(), bundle_output)} (${chalk_1.default.green(Math.round(stats1.size / 1024) + 'KB')})`);
77 log(`Done!`);
78 log(`Zipping it up into a FAB`);
79 const zipfile = path.resolve(output_file);
80 const options = {
81 includes: ['./server.js', './_assets/**'],
82 cwd: build_dir
83 };
84 await zip(build_dir, zipfile, options);
85 const stats2 = await fs.stat(zipfile);
86 log(` ${path.relative(process.cwd(), zipfile)} (${chalk_1.default.green(Math.round(stats2.size / 1024) + 'KB')})`);
87 log(chalk_1.default.green(`All done!`));
88 }
89 static async webpack(server_path, settings_path, build_path, renames) {
90 const settings_exists = await fs.pathExists(settings_path);
91 if (settings_exists) {
92 log(` Found production settings file at ${chalk_1.default.yellow(path.relative(process.cwd(), settings_path))}`);
93 }
94 await new Promise((resolve, reject) => webpack({
95 mode: 'production',
96 target: 'webworker',
97 entry: path.resolve(__dirname, 'files/public-redirect-wrapper.js'),
98 optimization: {
99 minimize: false
100 },
101 resolve: {
102 alias: {
103 fs: 'memfs',
104 'app-index': server_path,
105 'production-settings.json': settings_exists ? settings_path : path.resolve(__dirname, 'files/default-production-settings.json'),
106 }
107 },
108 output: {
109 path: build_path,
110 filename: 'server.js',
111 library: 'server',
112 libraryTarget: 'commonjs2'
113 },
114 node: {
115 path: true,
116 process: true,
117 net: 'empty'
118 },
119 plugins: [
120 new webpack.DefinePlugin({
121 FAB_REWRITES: JSON.stringify(renames)
122 })
123 ]
124 }, (err, stats) => {
125 if (err || stats.hasErrors()) {
126 console.log('Build failed.');
127 console.log(err);
128 console.log(stats.toJson().errors.toString());
129 reject();
130 }
131 resolve();
132 }));
133 }
134}
135exports.default = Compiler;