/** * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @format */ import { argv, cleanTask, logger, jestTask, option, parallel, series, spawn, task, tscTask, } from 'just-scripts'; import {readFile, writeFile} from 'fs/promises'; import glob from 'glob'; import path from 'path'; import which from 'which'; const node = process.execPath; option('fix'); task('clean', cleanTask({paths: ['build', 'dist']})); function defineFlavor(flavor: string, env: NodeJS.ProcessEnv) { task(`cmake-build:${flavor}`, cmakeBuildTask({targets: [flavor]})); task( `jest:${flavor}`, jestTask({config: path.join(__dirname, 'jest.config.ts'), env}), ); task( `test:${flavor}`, series(emcmakeGenerateTask(), `cmake-build:${flavor}`, `jest:${flavor}`), ); } defineFlavor('asmjs-async-node', {WASM: '0', SYNC: '0'}); defineFlavor('asmjs-sync-node', {WASM: '0', SYNC: '1'}); defineFlavor('asmjs-async-web', {WASM: '0', SYNC: '0'}); defineFlavor('asmjs-sync-web', {WASM: '0', SYNC: '1'}); defineFlavor('wasm-async-node', {WASM: '1', SYNC: '0'}); defineFlavor('wasm-sync-node', {WASM: '1', SYNC: '1'}); defineFlavor('wasm-async-web', {WASM: '1', SYNC: '0'}); defineFlavor('wasm-sync-web', {WASM: '1', SYNC: '1'}); task('build', series(emcmakeGenerateTask(), cmakeBuildTask())); task( 'test', series( emcmakeGenerateTask(), series('cmake-build:asmjs-async-node', 'jest:asmjs-async-node'), series('cmake-build:asmjs-sync-node', 'jest:asmjs-sync-node'), series('cmake-build:wasm-async-node', 'jest:wasm-async-node'), series('cmake-build:wasm-sync-node', 'jest:wasm-sync-node'), ), ); task( 'benchmark', series( emcmakeGenerateTask(), cmakeBuildTask({targets: ['asmjs-sync-node', 'wasm-sync-node']}), runBenchTask(), ), ); task('clang-format', clangFormatTask({fix: argv().fix})); task('prepack-package-json', async () => { const packageJsonPath = path.join(__dirname, 'package.json'); const packageJsonContents = await readFile(packageJsonPath); const packageJson = JSON.parse(packageJsonContents.toString('utf-8')); recursiveReplace(packageJson, /(.\/src\/.*)\.ts/, '$1.js'); await writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2)); }); task( 'prepack', series( parallel('build', tscTask({emitDeclarationOnly: true})), babelTransformTask({dir: 'src'}), 'prepack-package-json', ), ); function recursiveReplace( obj: Record, pattern: RegExp, replacement: string, ) { for (const [key, value] of Object.entries(obj)) { if (typeof value === 'string') { obj[key] = value.replace(pattern, replacement); } else if (typeof value === 'object' && value != null) { recursiveReplace(value as Record, pattern, replacement); } } } function babelTransformTask(opts: {dir: string}) { return () => { const args = [ opts.dir, '--source-maps', '--out-dir', opts.dir, '--extensions', '.js,.ts', ]; logger.info(`Transforming "${path.resolve(opts.dir)}"`); return spawn(node, [require.resolve('@babel/cli/bin/babel'), ...args], { cwd: __dirname, }); }; } function runBenchTask() { return () => { const files = glob.sync('./tests/Benchmarks/**/*'); const args = [ '--extensions', '.js,.ts', '--config-file', path.join(__dirname, '.babelrc.js'), '--', './tests/bin/run-bench.ts', ...files, ]; logger.info(['babel-node', ...args].join(' ')); return spawn( node, [require.resolve('@babel/node/bin/babel-node'), ...args], { stdio: 'inherit', }, ); }; } function emcmakeGenerateTask() { return () => { const emcmake = which.sync('emcmake'); const ninja = which.sync('ninja', {nothrow: true}); const args = [ 'cmake', '-S', '.', '-B', 'build', ...(ninja ? ['-G', 'Ninja'] : []), ]; logger.info(['emcmake', ...args].join(' ')); return spawn(emcmake, args, {stdio: 'inherit'}); }; } function cmakeBuildTask(opts?: {targets?: ReadonlyArray}) { return () => { const cmake = which.sync('cmake'); const args = [ '--build', 'build', ...(opts?.targets ? ['--target', ...opts.targets] : []), ]; logger.info(['cmake', ...args].join(' ')); return spawn(cmake, args, {stdio: 'inherit'}); }; } function clangFormatTask(opts?: {fix?: boolean}) { return () => { const args = [ ...(opts?.fix ? ['-i'] : ['--dry-run', '--Werror']), ...glob.sync('**/*.{h,hh,hpp,c,cpp,cc,m,mm}'), ]; logger.info(['clang-format', ...args].join(' ')); return spawn(node, [require.resolve('clang-format'), ...args], { stdio: 'inherit', }); }; }