1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | const fs = require("fs-extra");
|
4 | const chalk_1 = require("chalk");
|
5 | const util = require("util");
|
6 | const globby = require("globby");
|
7 | const path = require("path");
|
8 |
|
9 | const _zip = require("deterministic-zip");
|
10 | const webpack = require("webpack");
|
11 | const hasha = require("hasha");
|
12 | const zip = util.promisify(_zip);
|
13 | const _log = (str) => `${chalk_1.default.gray(`[FAB:Compile]`)} ${str
|
14 | .split(/\n\s*/)
|
15 | .join('\n ')}`;
|
16 | const log = (str) => console.log(_log(str));
|
17 | const error = (str) => console.log(_log(chalk_1.default.red(str)));
|
18 | class 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 | }
|
135 | exports.default = Compiler;
|