summaryrefslogtreecommitdiff
path: root/utils/js2common.js
blob: aeba49d9fd57f852738c69d5f2439acf16c4f9f6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#!/usr/bin/env node

const path = require('path');
const program = require('commander');
const fs = require('fs');
const fse = require('fs-extra');
const babel = require('@babel/core');

program
    .option('-m, --with-source-maps [type]', 'output source maps when not generating a bundled app (type may be empty for external source maps, inline for inline source maps, or both) ')
    .option('--clean', 'clear the lib folder before building')
    .parse(process.argv);

// the various important paths
const paths = {
    main: path.resolve(__dirname, '..'),
    core: path.resolve(__dirname, '..', 'core'),
    vendor: path.resolve(__dirname, '..', 'vendor'),
    libDirBase: path.resolve(__dirname, '..', 'lib'),
};

// util.promisify requires Node.js 8.x, so we have our own
function promisify(original) {
    return function promiseWrap() {
        const args = Array.prototype.slice.call(arguments);
        return new Promise((resolve, reject) => {
            original.apply(this, args.concat((err, value) => {
                if (err) return reject(err);
                resolve(value);
            }));
        });
    };
}

const writeFile = promisify(fs.writeFile);

const readdir = promisify(fs.readdir);
const lstat = promisify(fs.lstat);

const ensureDir = promisify(fse.ensureDir);

const babelTransformFile = promisify(babel.transformFile);

// walkDir *recursively* walks directories trees,
// calling the callback for all normal files found.
function walkDir(basePath, cb, filter) {
    return readdir(basePath)
        .then((files) => {
            const paths = files.map(filename => path.join(basePath, filename));
            return Promise.all(paths.map(filepath => lstat(filepath)
                .then((stats) => {
                    if (filter !== undefined && !filter(filepath, stats)) return;

                    if (stats.isSymbolicLink()) return;
                    if (stats.isFile()) return cb(filepath);
                    if (stats.isDirectory()) return walkDir(filepath, cb, filter);
                })));
        });
}

function makeLibFiles(sourceMaps) {
    // NB: we need to make a copy of babelOpts, since babel sets some defaults on it
    const babelOpts = () => ({
        plugins: [],
        presets: [
            [ '@babel/preset-env',
              { modules: 'commonjs' } ]
        ],
        ast: false,
        sourceMaps: sourceMaps,
    });

    fse.ensureDirSync(paths.libDirBase);

    const outFiles = [];

    const handleDir = (vendorRewrite, inPathBase, filename) => Promise.resolve()
        .then(() => {
            const outPath = path.join(paths.libDirBase, path.relative(inPathBase, filename));

            if (path.extname(filename) !== '.js') {
                return;  // skip non-javascript files
            }
            return Promise.resolve()
                .then(() => ensureDir(path.dirname(outPath)))
                .then(() => {
                    const opts = babelOpts();
            // Adjust for the fact that we move the core files relative
            // to the vendor directory
                    if (vendorRewrite) {
                        opts.plugins.push(["import-redirect",
                                           {"root": paths.libDirBase,
                                            "redirect": { "vendor/(.+)": "./vendor/$1"}}]);
                    }

                    return babelTransformFile(filename, opts)
                        .then((res) => {
                            console.log(`Writing ${outPath}`);
                            const {map} = res;
                            let {code} = res;
                            if (sourceMaps === true) {
                    // append URL for external source map
                                code += `\n//# sourceMappingURL=${path.basename(outPath)}.map\n`;
                            }
                            outFiles.push(`${outPath}`);
                            return writeFile(outPath, code)
                                .then(() => {
                                    if (sourceMaps === true || sourceMaps === 'both') {
                                        console.log(`  and ${outPath}.map`);
                                        outFiles.push(`${outPath}.map`);
                                        return writeFile(`${outPath}.map`, JSON.stringify(map));
                                    }
                                });
                        });
                });
        });

    Promise.resolve()
        .then(() => {
            const handler = handleDir.bind(null, false, paths.main);
            return walkDir(paths.vendor, handler);
        })
        .then(() => {
            const handler = handleDir.bind(null, true, paths.core);
            return walkDir(paths.core, handler);
        })
        .catch((err) => {
            console.error(`Failure converting modules: ${err}`);
            process.exit(1);
        });
}

let options = program.opts();

if (options.clean) {
    console.log(`Removing ${paths.libDirBase}`);
    fse.removeSync(paths.libDirBase);
}

makeLibFiles(options.withSourceMaps);