2022-08-10 04:41:44 +03:00
|
|
|
/**
|
2023-03-15 06:42:14 +03:00
|
|
|
* @file Configuration for the esbuild bundler and build/watch commands.
|
2022-08-10 04:41:44 +03:00
|
|
|
*
|
|
|
|
* The bundler processes each entry point into a single file, each with no external dependencies and
|
|
|
|
* minified. This primarily involves resolving all imports, along with some other transformations
|
|
|
|
* (like TypeScript compilation).
|
|
|
|
*
|
|
|
|
* See the bundlers documentation for more information:
|
2023-03-15 06:42:14 +03:00
|
|
|
* https://esbuild.github.io/getting-started/#bundling-for-node.
|
2022-08-10 04:41:44 +03:00
|
|
|
*/
|
|
|
|
|
2023-03-15 06:42:14 +03:00
|
|
|
import * as childProcess from 'node:child_process'
|
2023-03-22 11:18:06 +03:00
|
|
|
import * as fs from 'node:fs/promises'
|
2023-03-15 06:42:14 +03:00
|
|
|
import * as path from 'node:path'
|
|
|
|
import * as url from 'node:url'
|
2022-08-10 04:41:44 +03:00
|
|
|
|
2023-03-15 06:42:14 +03:00
|
|
|
import * as esbuild from 'esbuild'
|
|
|
|
import * as esbuildPluginCopy from 'enso-copy-plugin'
|
|
|
|
import * as esbuildPluginNodeGlobals from '@esbuild-plugins/node-globals-polyfill'
|
|
|
|
import * as esbuildPluginNodeModules from '@esbuild-plugins/node-modules-polyfill'
|
|
|
|
import esbuildPluginAlias from 'esbuild-plugin-alias'
|
|
|
|
import esbuildPluginTime from 'esbuild-plugin-time'
|
|
|
|
import esbuildPluginYaml from 'esbuild-plugin-yaml'
|
2022-08-10 04:41:44 +03:00
|
|
|
|
2023-03-29 14:20:46 +03:00
|
|
|
import * as esbuildWatch from '../../esbuild-watch.js'
|
2023-03-15 06:42:14 +03:00
|
|
|
import * as utils from '../../utils.js'
|
|
|
|
import BUILD_INFO from '../../build.json' assert { type: 'json' }
|
2022-08-10 04:41:44 +03:00
|
|
|
|
2023-03-15 06:42:14 +03:00
|
|
|
export const THIS_PATH = path.resolve(path.dirname(url.fileURLToPath(import.meta.url)))
|
2022-08-10 04:41:44 +03:00
|
|
|
|
2023-03-29 14:20:46 +03:00
|
|
|
// =================
|
|
|
|
// === Constants ===
|
|
|
|
// =================
|
|
|
|
|
|
|
|
const TAILWIND_BINARY_PATH = '../../node_modules/.bin/tailwindcss'
|
|
|
|
const TAILWIND_CSS_PATH = path.resolve(THIS_PATH, 'src', 'tailwind.css')
|
|
|
|
|
2022-08-10 04:41:44 +03:00
|
|
|
// =============================
|
|
|
|
// === Environment variables ===
|
|
|
|
// =============================
|
|
|
|
|
2023-03-03 01:00:47 +03:00
|
|
|
export interface Arguments {
|
|
|
|
/** List of files to be copied from WASM artifacts. */
|
2023-03-15 06:42:14 +03:00
|
|
|
wasmArtifacts: string
|
2023-03-03 01:00:47 +03:00
|
|
|
/** Directory with assets. Its contents are to be copied. */
|
2023-03-15 06:42:14 +03:00
|
|
|
assetsPath: string
|
2023-03-03 01:00:47 +03:00
|
|
|
/** Path where bundled files are output. */
|
2023-03-15 06:42:14 +03:00
|
|
|
outputPath: string
|
2023-03-03 01:00:47 +03:00
|
|
|
/** The main JS bundle to load WASM and JS wasm-pack bundles. */
|
2023-03-15 06:42:14 +03:00
|
|
|
ensoglAppPath: string
|
2023-03-03 01:00:47 +03:00
|
|
|
}
|
2022-08-10 04:41:44 +03:00
|
|
|
|
2023-03-15 06:42:14 +03:00
|
|
|
/**
|
|
|
|
* Get arguments from the environment.
|
|
|
|
*/
|
2023-03-03 01:00:47 +03:00
|
|
|
export function argumentsFromEnv(): Arguments {
|
2023-03-15 06:42:14 +03:00
|
|
|
const wasmArtifacts = utils.requireEnv('ENSO_BUILD_GUI_WASM_ARTIFACTS')
|
|
|
|
const assetsPath = utils.requireEnv('ENSO_BUILD_GUI_ASSETS')
|
|
|
|
const outputPath = path.resolve(utils.requireEnv('ENSO_BUILD_GUI'), 'assets')
|
|
|
|
const ensoglAppPath = utils.requireEnv('ENSO_BUILD_GUI_ENSOGL_APP')
|
|
|
|
return { wasmArtifacts, assetsPath, outputPath, ensoglAppPath }
|
2023-03-03 01:00:47 +03:00
|
|
|
}
|
2023-01-27 03:09:09 +03:00
|
|
|
|
2022-08-10 04:41:44 +03:00
|
|
|
// ===================
|
|
|
|
// === Git process ===
|
|
|
|
// ===================
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get output of a git command.
|
2023-03-15 06:42:14 +03:00
|
|
|
* @param command - Command line following the `git` program.
|
2022-08-10 04:41:44 +03:00
|
|
|
* @returns Output of the command.
|
|
|
|
*/
|
|
|
|
function git(command: string): string {
|
|
|
|
// TODO [mwu] Eventually this should be removed, data should be provided by the build script through `BUILD_INFO`.
|
|
|
|
// The bundler configuration should not invoke git, it is not its responsibility.
|
2023-03-15 06:42:14 +03:00
|
|
|
return childProcess.execSync(`git ${command}`, { encoding: 'utf8' }).trim()
|
2022-08-10 04:41:44 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// ==============================
|
|
|
|
// === Files to manually copy ===
|
|
|
|
// ==============================
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Static set of files that are always copied to the output directory.
|
|
|
|
*/
|
2023-03-15 06:42:14 +03:00
|
|
|
export function alwaysCopiedFiles(wasmArtifacts: string) {
|
2023-03-03 01:00:47 +03:00
|
|
|
return [
|
2023-03-15 06:42:14 +03:00
|
|
|
path.resolve(THIS_PATH, 'src', 'index.html'),
|
|
|
|
path.resolve(THIS_PATH, 'src', 'run.js'),
|
|
|
|
path.resolve(THIS_PATH, 'src', 'style.css'),
|
|
|
|
path.resolve(THIS_PATH, 'src', 'docsStyle.css'),
|
2023-03-29 14:20:46 +03:00
|
|
|
path.resolve(THIS_PATH, 'src', 'tailwind.css'),
|
2023-03-15 06:42:14 +03:00
|
|
|
...wasmArtifacts.split(path.delimiter),
|
2023-03-03 01:00:47 +03:00
|
|
|
]
|
|
|
|
}
|
2022-08-10 04:41:44 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Generator that yields all files that should be copied to the output directory.
|
2023-03-15 06:42:14 +03:00
|
|
|
* @yields {string} The file path of the next file to be copied.
|
2022-08-10 04:41:44 +03:00
|
|
|
*/
|
2023-03-15 06:42:14 +03:00
|
|
|
export async function* filesToCopyProvider(wasmArtifacts: string, assetsPath: string) {
|
2022-08-10 04:41:44 +03:00
|
|
|
console.log('Preparing a new generator for files to copy.')
|
2023-03-15 06:42:14 +03:00
|
|
|
yield* alwaysCopiedFiles(wasmArtifacts)
|
2023-03-22 11:18:06 +03:00
|
|
|
for (const file of await fs.readdir(assetsPath)) {
|
2023-03-15 06:42:14 +03:00
|
|
|
yield path.resolve(assetsPath, file)
|
2022-08-10 04:41:44 +03:00
|
|
|
}
|
|
|
|
console.log('Generator for files to copy finished.')
|
|
|
|
}
|
|
|
|
|
2023-03-29 14:20:46 +03:00
|
|
|
// ======================
|
|
|
|
// === Inline plugins ===
|
|
|
|
// ======================
|
|
|
|
|
|
|
|
function esbuildPluginGenerateTailwind(args: Pick<Arguments, 'assetsPath'>): esbuild.Plugin {
|
|
|
|
return {
|
|
|
|
name: 'enso-generate-tailwind',
|
|
|
|
setup: build => {
|
|
|
|
// Required since `onStart` is called on every rebuild.
|
|
|
|
let firstRun = true
|
|
|
|
build.onStart(() => {
|
|
|
|
if (firstRun) {
|
|
|
|
const dest = path.join(args.assetsPath, 'tailwind.css')
|
|
|
|
const config = path.resolve(THIS_PATH, 'tailwind.config.ts')
|
|
|
|
console.log(`Generating tailwind css from '${TAILWIND_CSS_PATH}' to '${dest}'.`)
|
|
|
|
const child = childProcess.spawn(`node`, [
|
|
|
|
TAILWIND_BINARY_PATH,
|
|
|
|
'-i',
|
|
|
|
TAILWIND_CSS_PATH,
|
|
|
|
'o',
|
|
|
|
dest,
|
|
|
|
'-c',
|
|
|
|
config,
|
|
|
|
'--minify',
|
|
|
|
])
|
|
|
|
firstRun = false
|
|
|
|
return new Promise(resolve =>
|
|
|
|
child.on('close', () => {
|
|
|
|
resolve({})
|
|
|
|
})
|
|
|
|
)
|
|
|
|
} else {
|
|
|
|
return {}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-10 04:41:44 +03:00
|
|
|
// ================
|
|
|
|
// === Bundling ===
|
|
|
|
// ================
|
|
|
|
|
2023-03-15 06:42:14 +03:00
|
|
|
/**
|
|
|
|
* Generate the builder options.
|
|
|
|
*/
|
2023-03-20 12:35:16 +03:00
|
|
|
export function bundlerOptions(args: Arguments) {
|
2023-03-15 06:42:14 +03:00
|
|
|
const { outputPath, ensoglAppPath, wasmArtifacts, assetsPath } = args
|
2023-03-29 14:20:46 +03:00
|
|
|
// This is required to make the `true` properties be typed as `boolean`.
|
|
|
|
// eslint-disable-next-line no-restricted-syntax
|
|
|
|
let trueBoolean = true as boolean
|
2023-03-20 12:35:16 +03:00
|
|
|
const buildOptions = {
|
|
|
|
// Disabling naming convention because these are third-party options.
|
2023-03-15 06:42:14 +03:00
|
|
|
/* eslint-disable @typescript-eslint/naming-convention */
|
|
|
|
absWorkingDir: THIS_PATH,
|
2023-03-29 14:20:46 +03:00
|
|
|
bundle: trueBoolean,
|
2023-03-15 06:42:14 +03:00
|
|
|
entryPoints: [path.resolve(THIS_PATH, 'src', 'index.ts')],
|
|
|
|
outdir: outputPath,
|
2023-03-03 01:00:47 +03:00
|
|
|
outbase: 'src',
|
|
|
|
plugins: [
|
2023-03-15 06:42:14 +03:00
|
|
|
esbuildPluginYaml.yamlPlugin({}),
|
|
|
|
esbuildPluginNodeModules.NodeModulesPolyfillPlugin(),
|
|
|
|
esbuildPluginNodeGlobals.NodeGlobalsPolyfillPlugin({ buffer: true, process: true }),
|
|
|
|
esbuildPluginAlias({ ensogl_app: ensoglAppPath }),
|
|
|
|
esbuildPluginTime(),
|
2023-03-29 14:20:46 +03:00
|
|
|
// This must run before the copy plugin so that the generated `tailwind.css` is used.
|
|
|
|
esbuildPluginGenerateTailwind({ assetsPath }),
|
2023-03-15 06:42:14 +03:00
|
|
|
esbuildPluginCopy.create(() => filesToCopyProvider(wasmArtifacts, assetsPath)),
|
2023-03-03 01:00:47 +03:00
|
|
|
],
|
|
|
|
define: {
|
|
|
|
GIT_HASH: JSON.stringify(git('rev-parse HEAD')),
|
|
|
|
GIT_STATUS: JSON.stringify(git('status --short --porcelain')),
|
|
|
|
BUILD_INFO: JSON.stringify(BUILD_INFO),
|
|
|
|
},
|
2023-03-29 14:20:46 +03:00
|
|
|
sourcemap: trueBoolean,
|
|
|
|
minify: trueBoolean,
|
|
|
|
metafile: trueBoolean,
|
2023-03-03 01:00:47 +03:00
|
|
|
format: 'esm',
|
2023-03-29 14:20:46 +03:00
|
|
|
publicPath: '/assets',
|
2023-03-03 01:00:47 +03:00
|
|
|
platform: 'browser',
|
2023-03-29 14:20:46 +03:00
|
|
|
incremental: trueBoolean,
|
|
|
|
color: trueBoolean,
|
2023-03-03 01:00:47 +03:00
|
|
|
logOverride: {
|
|
|
|
// Happens in Emscripten-generated MSDF (msdfgen_wasm.js):
|
|
|
|
// 1 │ ...typeof module!=="undefined"){module["exports"]=Module}process["o...
|
|
|
|
'commonjs-variable-in-esm': 'silent',
|
|
|
|
// Happens in Emscripten-generated MSDF (msdfgen_wasm.js):
|
|
|
|
// 1 │ ...y{table.grow(1)}catch(err){if(!err instanceof RangeError){throw ...
|
|
|
|
'suspicious-boolean-not': 'silent',
|
|
|
|
},
|
2023-03-15 06:42:14 +03:00
|
|
|
/* eslint-enable @typescript-eslint/naming-convention */
|
2023-03-20 12:35:16 +03:00
|
|
|
} satisfies esbuild.BuildOptions
|
|
|
|
// The narrower type is required to avoid non-null assertions elsewhere.
|
|
|
|
// The intersection with `esbuild.BuildOptions` is required to allow mutation.
|
|
|
|
const correctlyTypedBuildOptions: esbuild.BuildOptions & typeof buildOptions = buildOptions
|
|
|
|
return correctlyTypedBuildOptions
|
2023-03-03 01:00:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/** The basic, common settings for the bundler, based on the environment variables.
|
|
|
|
*
|
|
|
|
* Note that they should be further customized as per the needs of the specific workflow (e.g. watch vs. build).
|
2023-03-15 06:42:14 +03:00
|
|
|
*/
|
2023-03-20 12:35:16 +03:00
|
|
|
export function bundlerOptionsFromEnv() {
|
2023-03-03 01:00:47 +03:00
|
|
|
return bundlerOptions(argumentsFromEnv())
|
2022-08-10 04:41:44 +03:00
|
|
|
}
|
|
|
|
|
2023-03-29 14:20:46 +03:00
|
|
|
/** ESBuild options for spawning a watcher, that will continuously rebuild the package. */
|
|
|
|
export function watchOptions(onRebuild?: () => void, inject?: esbuild.BuildOptions['inject']) {
|
|
|
|
return esbuildWatch.toWatchOptions(bundlerOptionsFromEnv(), onRebuild, inject)
|
|
|
|
}
|
|
|
|
|
2023-03-03 01:00:47 +03:00
|
|
|
/** ESBuild options for bundling (one-off build) the package.
|
|
|
|
*
|
|
|
|
* Relies on the environment variables to be set.
|
2022-08-10 04:41:44 +03:00
|
|
|
*/
|
2023-03-03 01:00:47 +03:00
|
|
|
export function bundleOptions() {
|
2023-03-29 14:20:46 +03:00
|
|
|
const ret = bundlerOptionsFromEnv()
|
|
|
|
ret.watch = false
|
|
|
|
ret.incremental = false
|
|
|
|
return ret
|
2022-08-10 04:41:44 +03:00
|
|
|
}
|
2023-03-29 14:20:46 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Bundles the package.
|
|
|
|
*/
|
|
|
|
export async function bundle() {
|
|
|
|
try {
|
|
|
|
return esbuild.build(bundleOptions())
|
|
|
|
} catch (error) {
|
|
|
|
console.error(error)
|
|
|
|
throw error
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export default { watchOptions, bundle, bundleOptions }
|