/ scripts / build.js
build.js
  1  #!/usr/bin/node
  2  
  3  const start = Date.now()
  4  
  5  const fs = require("fs")
  6  const { readdir, copyFile, mkdir } = require("node:fs/promises")
  7  const path = require("path")
  8  
  9  const { build } = require("esbuild")
 10  const { compile, preprocess } = require("svelte/compiler")
 11  const sveltePreprocess = require("svelte-preprocess")
 12  const { loadTsConfig } = require("load-tsconfig")
 13  
 14  const {
 15    default: TsconfigPathsPlugin,
 16  } = require("@esbuild-plugins/tsconfig-paths")
 17  const { nodeExternalsPlugin } = require("esbuild-node-externals")
 18  
 19  const svelteCompilePlugin = {
 20    name: "svelteCompile",
 21    setup(build) {
 22      // Compiles `.svelte` files into JS classes so that they can be directly imported into our
 23      // Typescript packages
 24      build.onLoad({ filter: /\.svelte$/ }, async args => {
 25        const source = await fs.promises.readFile(args.path, "utf8")
 26        const dir = path.dirname(args.path)
 27  
 28        try {
 29          const preprocessed = await preprocess(
 30            source,
 31            sveltePreprocess({ filename: args.path })
 32          )
 33          const { js } = compile(preprocessed.code, {
 34            css: "injected",
 35            generate: "server",
 36          })
 37  
 38          return {
 39            // The code placed in the generated file
 40            contents: js.code,
 41            // The loader this is passed to, basically how the above provided content is "treated",
 42            // the contents provided above will be transpiled and bundled like any other JS file.
 43            loader: "js",
 44            // Where to resolve any imports present in the loaded file
 45            resolveDir: dir,
 46          }
 47        } catch (e) {
 48          return {
 49            errors: [
 50              {
 51                text: e && e.message ? e.message : String(e),
 52                detail: e,
 53              },
 54            ],
 55          }
 56        }
 57      })
 58    },
 59  }
 60  
 61  let { argv } = require("yargs")
 62  
 63  async function runBuild(entry, outfile, opts = {}) {
 64    const isDev = !process.env.CI
 65  
 66    console.log(`Building in mode dev mode: ${isDev}`)
 67  
 68    const tsconfig = argv["p"] || `tsconfig.build.json`
 69  
 70    const { data: tsconfigPathPluginContent } = loadTsConfig(
 71      process.cwd(),
 72      tsconfig
 73    )
 74    const external = Array.isArray(opts.external) ? opts.external : []
 75  
 76    const sharedConfig = {
 77      entryPoints: [entry],
 78      bundle: true,
 79      minify: !isDev,
 80      keepNames: true,
 81      sourcemap: tsconfigPathPluginContent.compilerOptions.sourceMap,
 82      sourcesContent: false,
 83      tsconfig,
 84      plugins: [
 85        svelteCompilePlugin,
 86        TsconfigPathsPlugin({ tsconfig: tsconfigPathPluginContent }),
 87        nodeExternalsPlugin({
 88          allowList: [
 89            "@budibase/frontend-core",
 90            "svelte",
 91            "chat",
 92            "@chat-adapter/discord",
 93            "@chat-adapter/slack",
 94            "@chat-adapter/teams",
 95            "@chat-adapter/state-ioredis",
 96            "@chat-adapter/state-memory",
 97          ],
 98        }),
 99      ],
100      preserveSymlinks: true,
101      metafile: true,
102      external,
103    }
104  
105    await mkdir("dist", { recursive: true })
106  
107    const hbsFiles = (async () => {
108      const dir = await readdir("./", { recursive: true })
109      const files = dir.filter(
110        entry => entry.endsWith(".hbs") || entry.endsWith("ivm.bundle.js")
111      )
112      const fileCopyPromises = files.map(file =>
113        copyFile(file, `dist/${path.basename(file)}`)
114      )
115  
116      await Promise.all(fileCopyPromises)
117    })()
118  
119    const mainBuild = build({
120      ...sharedConfig,
121      platform: "node",
122      outfile,
123    })
124  
125    await Promise.all([hbsFiles, mainBuild])
126  
127    if (isDev) {
128      fs.writeFileSync(
129        `dist/${path.basename(outfile)}.meta.json`,
130        JSON.stringify((await mainBuild).metafile)
131      )
132    }
133  
134    console.log(
135      "\x1b[32m%s\x1b[0m",
136      `Build successfully in ${(Date.now() - start) / 1000} seconds`
137    )
138  }
139  
140  if (require.main === module) {
141    const entry = argv["e"] || "./src/index.ts"
142    const outfile = `dist/${entry.split("/").pop().replace(".ts", ".js")}`
143    runBuild(entry, outfile)
144  } else {
145    module.exports = runBuild
146  }