From c4c20d66f9494f7e3ab357218231cedb3afca240 Mon Sep 17 00:00:00 2001 From: Herrington Darkholme <2883231+HerringtonDarkholme@users.noreply.github.com> Date: Tue, 14 Mar 2023 01:16:48 +0800 Subject: [PATCH] feat: support configuration file written in TypeScript (#2227) * chore: add editorconfig to the project This unifies all editors configuration * refactor: move loadConfig out of rspack-cli * feat: support using ts as configuration file * test: add test for typescript config file fix #2188 * feat: report ts-node require error --- .changeset/moody-cameras-act.md | 5 ++ .editorconfig | 19 +++++ packages/rspack-cli/package.json | 5 +- packages/rspack-cli/src/rspack-cli.ts | 46 +--------- packages/rspack-cli/src/utils/loadConfig.ts | 83 +++++++++++++++++++ .../rspack-cli/tests/build/typescript/main.ts | 1 + .../tests/build/typescript/rspack.config.ts | 12 +++ .../tests/build/typescript/tsconfig.json | 5 ++ .../tests/build/typescript/typescript.test.ts | 26 ++++++ packages/rspack-cli/tsconfig.json | 2 +- 10 files changed, 158 insertions(+), 46 deletions(-) create mode 100644 .changeset/moody-cameras-act.md create mode 100644 .editorconfig create mode 100644 packages/rspack-cli/src/utils/loadConfig.ts create mode 100644 packages/rspack-cli/tests/build/typescript/main.ts create mode 100644 packages/rspack-cli/tests/build/typescript/rspack.config.ts create mode 100644 packages/rspack-cli/tests/build/typescript/tsconfig.json create mode 100644 packages/rspack-cli/tests/build/typescript/typescript.test.ts diff --git a/.changeset/moody-cameras-act.md b/.changeset/moody-cameras-act.md new file mode 100644 index 00000000000..c627e139e50 --- /dev/null +++ b/.changeset/moody-cameras-act.md @@ -0,0 +1,5 @@ +--- +"@rspack/cli": patch +--- + +Support TypeScript as configuration file. diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000000..840d866031c --- /dev/null +++ b/.editorconfig @@ -0,0 +1,19 @@ +root = true + +[*.{js,ts}] +indent_style = tab +indent_size = 2 +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true +max_line_length = 80 + +[*.{yml,yaml,json}] +indent_style = space +indent_size = 2 + +[test/cases/parsing/bom/bomfile.{css,js}] +charset = utf-8-bom + +[*.md] +trim_trailing_whitespace = false diff --git a/packages/rspack-cli/package.json b/packages/rspack-cli/package.json index 48c789d7554..b4889a35b93 100644 --- a/packages/rspack-cli/package.json +++ b/packages/rspack-cli/package.json @@ -29,6 +29,9 @@ "source-map-support": "^0.5.19", "ts-node": "10.9.1" }, + "peerDependencies": { + "ts-node": "10.9.1" + }, "dependencies": { "@discoveryjs/json-ext": "^0.5.7", "@rspack/core": "workspace:*", @@ -37,4 +40,4 @@ "webpack-bundle-analyzer": "4.6.1", "yargs": "17.6.2" } -} \ No newline at end of file +} diff --git a/packages/rspack-cli/src/rspack-cli.ts b/packages/rspack-cli/src/rspack-cli.ts index c1fd9d5262a..86b33edeb0b 100644 --- a/packages/rspack-cli/src/rspack-cli.ts +++ b/packages/rspack-cli/src/rspack-cli.ts @@ -1,27 +1,20 @@ import { hideBin } from "yargs/helpers"; import yargs from "yargs"; import util from "util"; -import path from "path"; -import fs from "fs"; import { RspackCLIColors, RspackCLILogger, RspackCLIOptions } from "./types"; import { BuildCommand } from "./commands/build"; import { ServeCommand } from "./commands/serve"; import { RspackOptions, - MultiCompilerOptions, - createMultiCompiler, - createCompiler, MultiCompiler, Compiler, rspack, MultiRspackOptions } from "@rspack/core"; import { normalizeEnv } from "./utils/options"; +import { loadRspackConfig } from "./utils/loadConfig"; import { Mode } from "@rspack/core/src/config"; -const defaultConfig = "rspack.config.js"; -const defaultEntry = "src/index.js"; -type Callback = (err: Error, res?: T) => void; type RspackEnv = "development" | "production"; export class RspackCLI { colors: RspackCLIColors; @@ -181,42 +174,7 @@ export class RspackCLI { async loadConfig( options: RspackCLIOptions ): Promise { - let loadedConfig: - | undefined - | RspackOptions - | MultiRspackOptions - | (( - env: Record, - argv: Record - ) => RspackOptions | MultiRspackOptions); - // if we pass config paras - if (options.config) { - const resolvedConfigPath = path.resolve(process.cwd(), options.config); - if (!fs.existsSync(resolvedConfigPath)) { - throw new Error(`config file "${resolvedConfigPath}" not exists`); - } - loadedConfig = require(resolvedConfigPath); - } else { - let defaultConfigPath = path.resolve(process.cwd(), defaultConfig); - if (fs.existsSync(defaultConfigPath)) { - loadedConfig = require(defaultConfigPath); - } else { - let entry: Record = {}; - if (options.entry) { - entry = { - main: options.entry.map(x => path.resolve(process.cwd(), x))[0] // Fix me when entry supports array - }; - } else { - entry = { - main: path.resolve(process.cwd(), defaultEntry) - }; - } - loadedConfig = { - entry - }; - } - } - + let loadedConfig = loadRspackConfig(options); if (options.configName) { const notFoundConfigNames: string[] = []; diff --git a/packages/rspack-cli/src/utils/loadConfig.ts b/packages/rspack-cli/src/utils/loadConfig.ts new file mode 100644 index 00000000000..62f6672ebac --- /dev/null +++ b/packages/rspack-cli/src/utils/loadConfig.ts @@ -0,0 +1,83 @@ +import path from "path"; +import fs from "fs"; +import { RspackCLIOptions } from "../types"; +import { RspackOptions, MultiRspackOptions } from "@rspack/core"; + +const supportedExtensions = [".js", ".ts"]; +const defaultConfig = "rspack.config"; +const defaultEntry = "src/index"; + +export type LoadedRspackConfig = + | undefined + | RspackOptions + | MultiRspackOptions + | (( + env: Record, + argv: Record + ) => RspackOptions | MultiRspackOptions); + +export function loadRspackConfig( + options: RspackCLIOptions +): LoadedRspackConfig { + let loadedConfig: LoadedRspackConfig; + // if we pass config paras + if (options.config) { + const resolvedConfigPath = path.resolve(process.cwd(), options.config); + if (!fs.existsSync(resolvedConfigPath)) { + throw new Error(`config file "${resolvedConfigPath}" not exists`); + } + loadedConfig = requireWithAdditionalExtension(resolvedConfigPath); + } else { + let defaultConfigPath = findFileWithSupportedExtensions( + path.resolve(process.cwd(), defaultConfig) + ); + if (defaultConfigPath != null) { + loadedConfig = requireWithAdditionalExtension(defaultConfigPath); + } else { + let entry: Record = {}; + if (options.entry) { + entry = { + main: options.entry.map(x => path.resolve(process.cwd(), x))[0] // Fix me when entry supports array + }; + } else { + const defaultEntryBase = path.resolve(process.cwd(), defaultEntry); + const defaultEntryPath = + findFileWithSupportedExtensions(defaultEntryBase) || + defaultEntryBase + ".js"; // default entry is js + entry = { + main: defaultEntryPath + }; + } + loadedConfig = { + entry + }; + } + } + return loadedConfig; +} + +// takes a basePath like `webpack.config`, return `webpack.config.{js,ts}` if +// exists. returns null if none of them exists +function findFileWithSupportedExtensions(basePath: string): string | null { + for (const extension of supportedExtensions) { + if (fs.existsSync(basePath + extension)) { + return basePath + extension; + } + } + return null; +} + +let hasRegisteredTS = false; +function requireWithAdditionalExtension(resolvedPath: string) { + if (resolvedPath.endsWith("ts") && !hasRegisteredTS) { + hasRegisteredTS = true; + let tsNode: any; + try { + tsNode = require("ts-node"); + } catch (e) { + throw new Error("`ts-node` is required to use TypeScript configuration."); + } + tsNode.register({ transpileOnly: true }); + } + return require(resolvedPath); +} diff --git a/packages/rspack-cli/tests/build/typescript/main.ts b/packages/rspack-cli/tests/build/typescript/main.ts new file mode 100644 index 00000000000..41d13d1a9a1 --- /dev/null +++ b/packages/rspack-cli/tests/build/typescript/main.ts @@ -0,0 +1 @@ +console.log("Main typescript file"); diff --git a/packages/rspack-cli/tests/build/typescript/rspack.config.ts b/packages/rspack-cli/tests/build/typescript/rspack.config.ts new file mode 100644 index 00000000000..06bc7746d8e --- /dev/null +++ b/packages/rspack-cli/tests/build/typescript/rspack.config.ts @@ -0,0 +1,12 @@ +import * as path from "path"; + +const config = { + mode: "production", + entry: "./main.ts", + output: { + path: path.resolve(__dirname, "dist"), + filename: "foo.bundle.js", + }, +}; + +export = config; diff --git a/packages/rspack-cli/tests/build/typescript/tsconfig.json b/packages/rspack-cli/tests/build/typescript/tsconfig.json new file mode 100644 index 00000000000..391488ab17f --- /dev/null +++ b/packages/rspack-cli/tests/build/typescript/tsconfig.json @@ -0,0 +1,5 @@ +{ + "compilerOptions": { + "module": "commonjs" + } +} diff --git a/packages/rspack-cli/tests/build/typescript/typescript.test.ts b/packages/rspack-cli/tests/build/typescript/typescript.test.ts new file mode 100644 index 00000000000..afffb0e4ae0 --- /dev/null +++ b/packages/rspack-cli/tests/build/typescript/typescript.test.ts @@ -0,0 +1,26 @@ +import { run } from "../../utils/test-utils"; +import { existsSync } from "fs"; +import { resolve } from "path"; + +describe("webpack cli", () => { + it("should support default config in typescript", async () => { + const { exitCode, stderr, stdout } = await run(__dirname, []); + + expect(stderr).toBeFalsy(); + expect(stdout).toBeTruthy(); + expect(exitCode).toBe(0); + expect(existsSync(resolve(__dirname, "dist/foo.bundle.js"))).toBeTruthy(); + }); + + it("should support specifying config in typescript", async () => { + const { exitCode, stderr, stdout } = await run(__dirname, [ + "-c", + "./rspack.config.ts" + ]); + + expect(stderr).toBeFalsy(); + expect(stdout).toBeTruthy(); + expect(exitCode).toBe(0); + expect(existsSync(resolve(__dirname, "dist/foo.bundle.js"))).toBeTruthy(); + }); +}); diff --git a/packages/rspack-cli/tsconfig.json b/packages/rspack-cli/tsconfig.json index a5a6f160f01..98f2f74c1e2 100644 --- a/packages/rspack-cli/tsconfig.json +++ b/packages/rspack-cli/tsconfig.json @@ -21,4 +21,4 @@ "path": "../rspack-dev-server" } ] -} \ No newline at end of file +}