const {transformSync} = require('@babel/core');
const {btoa} = require('base64');
const {
lstatSync,
mkdirSync,
readdirSync,
readFileSync,
writeFileSync,
} = require('fs');
const {emptyDirSync} = require('fs-extra');
const {resolve} = require('path');
const rollup = require('rollup');
const babel = require('@rollup/plugin-babel').babel;
const commonjs = require('@rollup/plugin-commonjs');
const jsx = require('acorn-jsx');
const rollupResolve = require('@rollup/plugin-node-resolve').nodeResolve;
const {encode, decode} = require('sourcemap-codec');
const {generateEncodedHookMap} = require('../generateHookMap');
const {parse} = require('@babel/parser');
const sourceDir = resolve(__dirname, '__source__');
const buildRoot = resolve(sourceDir, '__compiled__');
const externalDir = resolve(buildRoot, 'external');
const inlineDir = resolve(buildRoot, 'inline');
const bundleDir = resolve(buildRoot, 'bundle');
const noColumnsDir = resolve(buildRoot, 'no-columns');
const inlineIndexMapDir = resolve(inlineDir, 'index-map');
const externalIndexMapDir = resolve(externalDir, 'index-map');
const inlineFbSourcesExtendedDir = resolve(inlineDir, 'fb-sources-extended');
const externalFbSourcesExtendedDir = resolve(
externalDir,
'fb-sources-extended',
);
const inlineFbSourcesIndexMapExtendedDir = resolve(
inlineFbSourcesExtendedDir,
'index-map',
);
const externalFbSourcesIndexMapExtendedDir = resolve(
externalFbSourcesExtendedDir,
'index-map',
);
const inlineReactSourcesExtendedDir = resolve(
inlineDir,
'react-sources-extended',
);
const externalReactSourcesExtendedDir = resolve(
externalDir,
'react-sources-extended',
);
const inlineReactSourcesIndexMapExtendedDir = resolve(
inlineReactSourcesExtendedDir,
'index-map',
);
const externalReactSourcesIndexMapExtendedDir = resolve(
externalReactSourcesExtendedDir,
'index-map',
);
emptyDirSync(buildRoot);
mkdirSync(externalDir);
mkdirSync(inlineDir);
mkdirSync(bundleDir);
mkdirSync(noColumnsDir);
mkdirSync(inlineIndexMapDir);
mkdirSync(externalIndexMapDir);
mkdirSync(inlineFbSourcesExtendedDir);
mkdirSync(externalFbSourcesExtendedDir);
mkdirSync(inlineReactSourcesExtendedDir);
mkdirSync(externalReactSourcesExtendedDir);
mkdirSync(inlineFbSourcesIndexMapExtendedDir);
mkdirSync(externalFbSourcesIndexMapExtendedDir);
mkdirSync(inlineReactSourcesIndexMapExtendedDir);
mkdirSync(externalReactSourcesIndexMapExtendedDir);
function compile(fileName) {
const code = readFileSync(resolve(sourceDir, fileName), 'utf8');
const transformed = transformSync(code, {
plugins: ['@babel/plugin-transform-modules-commonjs'],
presets: [
[
'@babel/react',
],
],
sourceMap: true,
});
const sourceMap = transformed.map;
sourceMap.sources = [fileName];
writeFileSync(
resolve(externalDir, fileName),
transformed.code +
`\n//# sourceMappingURL=${fileName}.map?foo=bar¶m=some_value`,
'utf8',
);
writeFileSync(
resolve(externalDir, `${fileName}.map`),
JSON.stringify(sourceMap),
'utf8',
);
writeFileSync(
resolve(inlineDir, fileName),
transformed.code +
'\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,' +
btoa(JSON.stringify(sourceMap)),
'utf8',
);
const decodedMappings = decode(sourceMap.mappings).map(entries =>
entries.map(entry => {
if (entry.length === 0) {
return entry;
}
return [...entry.slice(0, 3), 0, ...entry.slice(4)];
}),
);
const encodedMappings = encode(decodedMappings);
writeFileSync(
resolve(noColumnsDir, fileName),
transformed.code +
'\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,' +
btoa(
JSON.stringify({
...sourceMap,
mappings: encodedMappings,
}),
),
'utf8',
);
const indexMap = {
version: sourceMap.version,
file: sourceMap.file,
sections: [
{
offset: {
line: 0,
column: 0,
},
map: {...sourceMap},
},
],
};
writeFileSync(
resolve(externalIndexMapDir, fileName),
transformed.code +
`\n//# sourceMappingURL=${fileName}.map?foo=bar¶m=some_value`,
'utf8',
);
writeFileSync(
resolve(externalIndexMapDir, `${fileName}.map`),
JSON.stringify(indexMap),
'utf8',
);
writeFileSync(
resolve(inlineIndexMapDir, fileName),
transformed.code +
'\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,' +
btoa(JSON.stringify(indexMap)),
'utf8',
);
const parsed = parse(code, {
sourceType: 'module',
plugins: ['jsx', 'flow'],
});
const encodedHookMap = generateEncodedHookMap(parsed);
const fbSourcesExtendedSourceMap = {
...sourceMap,
x_facebook_sources: [[null, [encodedHookMap]]],
};
const fbSourcesExtendedIndexMap = {
version: fbSourcesExtendedSourceMap.version,
file: fbSourcesExtendedSourceMap.file,
sections: [
{
offset: {
line: 0,
column: 0,
},
map: {...fbSourcesExtendedSourceMap},
},
],
};
const reactSourcesExtendedSourceMap = {
...sourceMap,
x_react_sources: [[encodedHookMap]],
};
const reactSourcesExtendedIndexMap = {
version: reactSourcesExtendedSourceMap.version,
file: reactSourcesExtendedSourceMap.file,
sections: [
{
offset: {
line: 0,
column: 0,
},
map: {...reactSourcesExtendedSourceMap},
},
],
};
writeFileSync(
resolve(inlineFbSourcesExtendedDir, fileName),
transformed.code +
'\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,' +
btoa(JSON.stringify(fbSourcesExtendedSourceMap)),
'utf8',
);
writeFileSync(
resolve(externalFbSourcesExtendedDir, fileName),
transformed.code +
`\n//# sourceMappingURL=${fileName}.map?foo=bar¶m=some_value`,
'utf8',
);
writeFileSync(
resolve(externalFbSourcesExtendedDir, `${fileName}.map`),
JSON.stringify(fbSourcesExtendedSourceMap),
'utf8',
);
writeFileSync(
resolve(inlineFbSourcesIndexMapExtendedDir, fileName),
transformed.code +
'\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,' +
btoa(JSON.stringify(fbSourcesExtendedIndexMap)),
'utf8',
);
writeFileSync(
resolve(externalFbSourcesIndexMapExtendedDir, fileName),
transformed.code +
`\n//# sourceMappingURL=${fileName}.map?foo=bar¶m=some_value`,
'utf8',
);
writeFileSync(
resolve(externalFbSourcesIndexMapExtendedDir, `${fileName}.map`),
JSON.stringify(fbSourcesExtendedIndexMap),
'utf8',
);
writeFileSync(
resolve(inlineReactSourcesExtendedDir, fileName),
transformed.code +
'\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,' +
btoa(JSON.stringify(reactSourcesExtendedSourceMap)),
'utf8',
);
writeFileSync(
resolve(externalReactSourcesExtendedDir, fileName),
transformed.code +
`\n//# sourceMappingURL=${fileName}.map?foo=bar¶m=some_value`,
'utf8',
);
writeFileSync(
resolve(externalReactSourcesExtendedDir, `${fileName}.map`),
JSON.stringify(reactSourcesExtendedSourceMap),
'utf8',
);
writeFileSync(
resolve(inlineReactSourcesIndexMapExtendedDir, fileName),
transformed.code +
'\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,' +
btoa(JSON.stringify(reactSourcesExtendedIndexMap)),
'utf8',
);
writeFileSync(
resolve(externalReactSourcesIndexMapExtendedDir, fileName),
transformed.code +
`\n//# sourceMappingURL=${fileName}.map?foo=bar¶m=some_value`,
'utf8',
);
writeFileSync(
resolve(externalReactSourcesIndexMapExtendedDir, `${fileName}.map`),
JSON.stringify(reactSourcesExtendedIndexMap),
'utf8',
);
}
async function bundle() {
const entryFileName = resolve(sourceDir, 'index.js');
const result = await rollup.rollup({
input: entryFileName,
acornInjectPlugins: [jsx()],
plugins: [
rollupResolve(),
commonjs(),
babel({
babelHelpers: 'bundled',
presets: ['@babel/preset-react'],
sourceMap: true,
}),
],
external: ['react'],
});
await result.write({
file: resolve(bundleDir, 'index.js'),
format: 'cjs',
sourcemap: true,
});
}
const entries = readdirSync(sourceDir);
entries.forEach(entry => {
const stat = lstatSync(resolve(sourceDir, entry));
if (!stat.isDirectory() && entry.endsWith('.js')) {
compile(entry);
}
});
bundle().catch(e => {
console.error(e);
process.exit(1);
});