diff --git a/packages/compiler/src/options.spec.ts b/packages/compiler/src/options.spec.ts index eb06546f..f497df73 100644 --- a/packages/compiler/src/options.spec.ts +++ b/packages/compiler/src/options.spec.ts @@ -39,7 +39,7 @@ describe("createOptions", () => { ); }); - it("should return expected path w/ custom addons directory", () => { + it("should return expected path w/ custom addons directory and '*' target", () => { const { addons } = compileSystem({ files: { "./expected/addon-foo/addon.js": "export const activate = () => {};", @@ -54,7 +54,7 @@ describe("createOptions", () => { { virtual: true } ); - const actual = addons.refresh().getAvailableAddons(); + const actual = addons.refresh().getAvailableAddons("*"); expect(actual.getNames()).toEqual(["addon-foo"]); }); diff --git a/packages/compiler/src/options.ts b/packages/compiler/src/options.ts index 15f09e14..8a28d714 100644 --- a/packages/compiler/src/options.ts +++ b/packages/compiler/src/options.ts @@ -31,7 +31,7 @@ export const createOptions = (args: CompilerArguments, reporter = new NoReporter const compilationConfig = resolveCompilationConfig(args.config ?? DEFAULTS.config, reporter, system); const projectDirectory = - (compilationConfig && dirname(compilationConfig.configFilePath)) ?? + (compilationConfig?.configFilePath && dirname(compilationConfig.configFilePath)) ?? (tsconfig.raw && tsconfig.raw.configFilePath && dirname(tsconfig.raw?.configFilePath)); tsconfig.options.outDir = args.buildDir ?? tsconfig.options.outDir ?? DEFAULTS.outDir; if (projectDirectory) { diff --git a/packages/core/src/compiler/Compiler.ts b/packages/core/src/compiler/Compiler.ts index 931a8b53..1ba99bae 100644 --- a/packages/core/src/compiler/Compiler.ts +++ b/packages/core/src/compiler/Compiler.ts @@ -76,10 +76,6 @@ export class Compiler { public setOptions(options: CompilerOptions): this { this.options = options; - if (!this.options?.targets || this.options.targets.length === 0) { - this.options.targets = ["*"]; - } - this.reporter = options.reporter ?? new DefaultReporter(this.system); this.compilationHost = new CompilationHost(createSharedHost(this.system) as ts.LanguageServiceHost); diff --git a/packages/core/src/compiler/CompilerOptions.ts b/packages/core/src/compiler/CompilerOptions.ts index f0c3aa5c..3a6c5b4d 100644 --- a/packages/core/src/compiler/CompilerOptions.ts +++ b/packages/core/src/compiler/CompilerOptions.ts @@ -16,7 +16,16 @@ export interface CompilerOptions { project: ts.CompilerOptions; reporter: Reporter; sourceMap: boolean; + /** + * List of targets to be compiled. Refers to the `targets` defined in the `config` property. + * Use `["*"]` to compile with all available addons. No addons will be used if no target is specified. + */ targets: string[]; + /** + * Whether to only transpile the code without emitting any output. + * Overrides the `transpileOnly` specified in the `tsconfig.json`. + * @deprecated use `config.transpileOnly` instead. + */ transpileOnly: boolean; watch: boolean; additionalArguments?: Map; diff --git a/packages/core/src/compiler/addons/AddonRegistry.spec.ts b/packages/core/src/compiler/addons/AddonRegistry.spec.ts index fcb1fe59..2b5423da 100644 --- a/packages/core/src/compiler/addons/AddonRegistry.spec.ts +++ b/packages/core/src/compiler/addons/AddonRegistry.spec.ts @@ -40,7 +40,7 @@ describe("getAvailableAddons", () => { it("returns empty addons w/ empty addons directory", () => { const testObj = new AddonRegistry({ addonsDir: "./addons", reporter, system }); - expect(testObj.getAvailableAddons()).toHaveLength(0); + expect(testObj.getAvailableAddons("*")).toHaveLength(0); }); it("returns addons w/ single addon in addon directory", () => { @@ -48,7 +48,7 @@ describe("getAvailableAddons", () => { const testObj = new AddonRegistry({ addonsDir: "./addons", reporter, system }).refresh(); - expect(testObj.getAvailableAddons().getNames()).toEqual(["expected"]); + expect(testObj.getAvailableAddons("*").getNames()).toEqual(["expected"]); }); it("returns addons w/ multiple addons in addon directory", () => { @@ -58,7 +58,7 @@ describe("getAvailableAddons", () => { const testObj = new AddonRegistry({ addonsDir: "./addons", reporter, system }).refresh(); - expect(testObj.getAvailableAddons().getNames()).toEqual(["one", "two", "three"]); + expect(testObj.getAvailableAddons("*").getNames()).toEqual(["one", "two", "three"]); }); it("returns valid addons w/ invalid and valid addons in addon directory", () => { @@ -67,7 +67,7 @@ describe("getAvailableAddons", () => { const testObj = new AddonRegistry({ addonsDir: "./addons", reporter, system }).refresh(); - expect(testObj.getAvailableAddons().getNames()).toEqual(["expected"]); + expect(testObj.getAvailableAddons("*").getNames()).toEqual(["expected"]); }); it("returns no addons w/ empty files in addon directory", () => { @@ -75,7 +75,7 @@ describe("getAvailableAddons", () => { const testObj = new AddonRegistry({ addonsDir: "./addons", reporter, system }).refresh(); - expect(testObj.getAvailableAddons()).toHaveLength(0); + expect(testObj.getAvailableAddons("*")).toHaveLength(0); }); it("reports warning w/ non-existing addons name", () => { @@ -87,7 +87,7 @@ describe("getAvailableAddons", () => { addonsDir: "./addons", reporter, system, - }).getAvailableAddons(); + }).getAvailableAddons("*"); expect(reporter.reportDiagnostic).toHaveBeenCalledWith(new WarnMessage('Missing addons: "does-not-exist".')); }); @@ -127,13 +127,13 @@ describe("refresh", () => { const testObj = new AddonRegistry({ addonsDir: "./addons", reporter, system }).refresh(); - expect(testObj.getAvailableAddons().getNames()).toEqual(["expected"]); + expect(testObj.getAvailableAddons("*").getNames()).toEqual(["expected"]); createAddon("addons/new/addon"); testObj.refresh(); - expect(testObj.getAvailableAddons().getNames()).toEqual(["expected", "new"]); + expect(testObj.getAvailableAddons("*").getNames()).toEqual(["expected", "new"]); }); it("refreshes addons w/o new addons", () => { @@ -141,11 +141,11 @@ describe("refresh", () => { const testObj = new AddonRegistry({ addonsDir: "./addons", reporter, system }).refresh(); - expect(testObj.getAvailableAddons().getNames()).toEqual(["expected"]); + expect(testObj.getAvailableAddons("*").getNames()).toEqual(["expected"]); testObj.refresh(); - expect(testObj.getAvailableAddons().getNames()).toEqual(["expected"]); + expect(testObj.getAvailableAddons("*").getNames()).toEqual(["expected"]); }); it("refreshes addons w/ removed addons", () => { @@ -154,13 +154,13 @@ describe("refresh", () => { const testObj = new AddonRegistry({ addonsDir: "./addons", reporter, system }).refresh(); - expect(testObj.getAvailableAddons().getNames()).toEqual(["expected", "removed"]); + expect(testObj.getAvailableAddons("*").getNames()).toEqual(["expected", "removed"]); removeAddon("addons/removed/addon"); testObj.refresh(); - expect(testObj.getAvailableAddons().getNames()).toEqual(["expected"]); + expect(testObj.getAvailableAddons("*").getNames()).toEqual(["expected"]); }); it("refreshes addons w/ empty addons directory", () => { @@ -168,13 +168,13 @@ describe("refresh", () => { const testObj = new AddonRegistry({ addonsDir: "./addons", reporter, system }).refresh(); - expect(testObj.getAvailableAddons().getNames()).toEqual(["expected"]); + expect(testObj.getAvailableAddons("*").getNames()).toEqual(["expected"]); removeAddon("addons/expected/addon"); testObj.refresh(); - expect(testObj.getAvailableAddons()).toHaveLength(0); + expect(testObj.getAvailableAddons("*")).toHaveLength(0); }); }); @@ -271,7 +271,7 @@ describe("reportMissingAddons", () => { system.createDirectory("./target"); reporter.reportDiagnostic = jest.fn(); - new AddonRegistry({ addonsDir: "./target", addons: ["missing"], reporter, system }).getAvailableAddons(); + new AddonRegistry({ addonsDir: "./target", addons: ["missing"], reporter, system }).getAvailableAddons("*"); expect(reporter.reportDiagnostic).toHaveBeenCalledWith(new WarnMessage('Missing addons: "missing".')); }); @@ -281,7 +281,7 @@ describe("reportMissingAddons", () => { createAddon("target/expected/addon"); reporter.reportDiagnostic = jest.fn(); - new AddonRegistry({ addonsDir: "./target", addons: ["expected"], reporter, system }).refresh().getAvailableAddons(); + new AddonRegistry({ addonsDir: "./target", addons: ["expected"], reporter, system }).refresh().getAvailableAddons("*"); expect(reporter.reportDiagnostic).not.toHaveBeenCalled(); }); @@ -324,7 +324,7 @@ describe("findAddons", () => { const testObj = new AddonRegistry({ addonsDir: "./addons", reporter, system }).refresh(); - expect(testObj.getAvailableAddons().getNames()).toEqual(["expected"]); + expect(testObj.getAvailableAddons("*").getNames()).toEqual(["expected"]); }); it("finds addons w/ multiple addons in addons directory", () => { @@ -334,7 +334,7 @@ describe("findAddons", () => { const testObj = new AddonRegistry({ addonsDir: "./addons", reporter, system }).refresh(); - expect(testObj.getAvailableAddons().getNames()).toEqual(["one", "two", "three"]); + expect(testObj.getAvailableAddons("*").getNames()).toEqual(["one", "two", "three"]); }); it("finds valid addons w/ invalid and valid addons in addons directory", () => { @@ -343,7 +343,7 @@ describe("findAddons", () => { const testObj = new AddonRegistry({ addonsDir: "./addons", reporter, system }).refresh(); - expect(testObj.getAvailableAddons().getNames()).toEqual(["expected"]); + expect(testObj.getAvailableAddons("*").getNames()).toEqual(["expected"]); }); it("finds no addons w/ empty files in addons directory", () => { @@ -351,7 +351,7 @@ describe("findAddons", () => { const testObj = new AddonRegistry({ addonsDir: "./addons", reporter, system }); - expect(testObj.getAvailableAddons()).toHaveLength(0); + expect(testObj.getAvailableAddons("*")).toHaveLength(0); }); }); diff --git a/packages/core/src/compiler/addons/AddonRegistry.ts b/packages/core/src/compiler/addons/AddonRegistry.ts index d0fee365..6c8189b1 100644 --- a/packages/core/src/compiler/addons/AddonRegistry.ts +++ b/packages/core/src/compiler/addons/AddonRegistry.ts @@ -39,9 +39,9 @@ export class AddonRegistry { this.reportMissingAddons(target); const expectedNames = this.getExpectedAddons(target); const results = - expectedNames.length > 0 - ? [...this.availableAddons].filter(([name]) => expectedNames.includes(name)).map(([, addon]) => addon) - : Array.from(this.availableAddons.values()); + expectedNames.length === 0 && target === "*" + ? Array.from(this.availableAddons.values()) + : [...this.availableAddons].filter(([name]) => expectedNames.includes(name)).map(([, addon]) => addon); return compilerAddons(results); } diff --git a/packages/core/src/compiler/config/CompilationConfig.ts b/packages/core/src/compiler/config/CompilationConfig.ts index 6bf6130b..9c4d58fa 100644 --- a/packages/core/src/compiler/config/CompilationConfig.ts +++ b/packages/core/src/compiler/config/CompilationConfig.ts @@ -7,9 +7,17 @@ import type { TargetConfig } from "@quatico/websmith-api"; export type CompilationConfig = { + /** List of addons to be loaded. Overrides the `addons` specified in the configuration file. */ addons?: string[]; + /** Relative path to the directory containing the addons. Overrides the `addonsDir` specified in the configuration file. */ addonsDir?: string; - configFilePath: string; + /** Relative path to the `websmith.config.json` configuration file. */ + configFilePath?: string; + /** Record of target specific addons configurations. Overrides the `targets` specified in the configuration file. */ targets?: Record; + /** + * Whether to only transpile the code without emitting any output. + * Overrides the `transpileOnly` specified in the `tsconfig.json`. + */ transpileOnly?: boolean; }; diff --git a/packages/core/test/compile-options.ts b/packages/core/test/compile-options.ts index 8a7af4b3..e74430d4 100644 --- a/packages/core/test/compile-options.ts +++ b/packages/core/test/compile-options.ts @@ -27,7 +27,7 @@ export const compileOptions = ( configFilePath: "./tsconfig.json", ...overrides?.project, }, - targets: overrides?.targets ?? [], + targets: overrides?.targets ?? ["*"], tsconfig: { options: {}, fileNames: system.readDirectory("./src"), diff --git a/packages/test/src/bar-addon.test.ts b/packages/test/src/bar-addon.test.ts index 76162cbd..d20f8168 100644 --- a/packages/test/src/bar-addon.test.ts +++ b/packages/test/src/bar-addon.test.ts @@ -11,7 +11,7 @@ describe("test-project-foo", () => { it("should install addon successfully", () => { const testObj = compilationEnv("__TEST__").addAddon("foo-addon", join(__dirname, "../test-data/addons")); - const actual = testObj.getActiveAddons().getNames(); + const actual = testObj.getActiveAddons("*").getNames(); expect(actual).toContain("foo-addon"); }); diff --git a/packages/testing/src/compilation/environment.spec.ts b/packages/testing/src/compilation/environment.spec.ts index c3c811b8..2c0d68af 100644 --- a/packages/testing/src/compilation/environment.spec.ts +++ b/packages/testing/src/compilation/environment.spec.ts @@ -48,7 +48,7 @@ describe("compilationEnv", () => { target: ts.ScriptTarget.ESNext, }, sourceMap: false, - targets: [], + targets: ["*"], transpileOnly: false, tsconfig: { errors: [], @@ -80,13 +80,13 @@ describe("compilationEnv#addons", () => { const testObj = compilationEnv("/target"); expect(testObj.getAddonsDir()).toBe("/target/addons"); - expect(testObj.getActiveAddons()).toHaveLength(0); + expect(testObj.getActiveAddons("*")).toHaveLength(0); }); it("should yield addon with valid addon source", () => { const testObj = compilationEnv("/target").addAddon("expected-addon", { "addon.ts": `export const activate = () => {};` }); - const actual = testObj.getActiveAddons().getNames(); + const actual = testObj.getActiveAddons("*").getNames(); expect(actual).toEqual(["expected-addon"]); }); @@ -95,7 +95,7 @@ describe("compilationEnv#addons", () => { console.warn = jest.fn(); const testObj = compilationEnv("/target").addAddon("invalid-addon", { "addon.ts": `export const NO_ACTIVATE_FUNCTION = true;` }); - const actual = testObj.getActiveAddons(); + const actual = testObj.getActiveAddons("*").getNames(); expect(actual).toHaveLength(0); }); @@ -106,7 +106,7 @@ describe("compilationEnv#addons", () => { testObj.addAddon("expected-addon"); - const actual = testObj.getActiveAddons().getNames(); + const actual = testObj.getActiveAddons("*").getNames(); expect(actual).toEqual(["expected-addon"]); }); @@ -118,7 +118,7 @@ describe("compilationEnv#addons", () => { testObj.addAddons(["expected-addon1", "expected-addon2"]); - const actual = testObj.getActiveAddons().getNames(); + const actual = testObj.getActiveAddons("*").getNames(); expect(actual).toEqual(["expected-addon1", "expected-addon2"]); }); @@ -130,7 +130,7 @@ describe("compilationEnv#addons", () => { testObj.addAddons(["expected-addon1", "expected-addon2"], "./custom-addons-folder/"); - const actual = testObj.getActiveAddons().getNames(); + const actual = testObj.getActiveAddons("*").getNames(); expect(actual).toEqual(["expected-addon1", "expected-addon2"]); }); diff --git a/packages/testing/src/compilation/environment.ts b/packages/testing/src/compilation/environment.ts index 99c0f00a..6512a7cc 100644 --- a/packages/testing/src/compilation/environment.ts +++ b/packages/testing/src/compilation/environment.ts @@ -61,6 +61,7 @@ export class CompilationEnv { this.compilerOptions = compileOptions(this.system, { buildDir: this.buildDir, + targets: options?.compilerOptions?.targets?.length ? options.compilerOptions.targets : ["*"], config: { configFilePath: `${this.rootDir}/websmith.config.json`, targets: { @@ -144,8 +145,8 @@ export class CompilationEnv { return this.addons?.getAvailableAddons().find((it: CompilerAddon) => it.getName() === addonName); } - public getActiveAddons(): CompilerAddons { - return this.addons?.getAvailableAddons() ?? compilerAddons([]); + public getActiveAddons(target?: string): CompilerAddons { + return this.addons?.getAvailableAddons(target) ?? compilerAddons([]); } /** @@ -330,6 +331,7 @@ export class CompilationEnv { esModuleInterop: true, moduleResolution: ts.ModuleResolutionKind.NodeNext, }, + targets: ["*"], tsconfig: { fileNames: this.system.readDirectory(curDir).filter(isSourceFile), options: {}, errors: [] }, }, this.system diff --git a/packages/webpack-test/src/compile-module-date.test.ts b/packages/webpack-test/src/compile-module-date.test.ts index 63155fa1..2b10c176 100644 --- a/packages/webpack-test/src/compile-module-date.test.ts +++ b/packages/webpack-test/src/compile-module-date.test.ts @@ -8,7 +8,7 @@ import { readdirSync, readFileSync, rmSync } from "fs"; import { resolve } from "path"; import { Configuration, NormalModule } from "webpack"; -import { createWebpackCompiler } from "./webpack-utils"; +import { webpackBuild } from "./webpack-utils"; describe("project bundling", () => { const projectDir = resolve(__dirname, "../__data__/module-test"); @@ -24,7 +24,7 @@ describe("project bundling", () => { }); it("yields modules", async () => { - const { stats, compiler } = await createWebpackCompiler(config, projectDir); + const { stats, compiler } = await webpackBuild(config, projectDir); expect( Array.from(stats!.compilation.modules.values()) @@ -36,7 +36,7 @@ describe("project bundling", () => { }); it("yields chunks", async () => { - const { stats, compiler } = await createWebpackCompiler(config, projectDir); + const { stats, compiler } = await webpackBuild(config, projectDir); expect(Array.from(stats!.compilation.chunks.values()).map(cur => cur.name)).toEqual(expect.arrayContaining(["functions", "main"])); @@ -44,7 +44,7 @@ describe("project bundling", () => { }); it("yields bundled output", async () => { - const { compiler } = await createWebpackCompiler(config, projectDir); + const { compiler } = await webpackBuild(config, projectDir); expect(readdirSync(resolve(__dirname, "../__data__/module-test/.build/lib"))).toEqual([ "functions.js", diff --git a/packages/webpack-test/src/loader.test.ts b/packages/webpack-test/src/loader.test.ts index 95d8f654..9cd0d61e 100644 --- a/packages/webpack-test/src/loader.test.ts +++ b/packages/webpack-test/src/loader.test.ts @@ -7,7 +7,7 @@ import { readFileSync, rmSync, statSync, writeFileSync } from "fs"; import { join, resolve } from "path"; import { Compiler, WebpackError, Configuration } from "webpack"; -import { createWebpackCompiler } from "./webpack-utils"; +import { webpackBuild } from "./webpack-utils"; const projectDir = resolve(__dirname, "../__data__/module-test"); const targetInput = resolve(projectDir, "src", "index.tsx"); @@ -26,7 +26,7 @@ afterEach(() => { describe("webpack loader", () => { console.info = jest.fn(); it("should throw an error if webpackTarget does not exist as target", async () => { - await createWebpackCompiler(requireWebpackConfig("webpack_unknownWebpackTarget.config.js"), projectDir) + await webpackBuild(requireWebpackConfig("webpack_unknownWebpackTarget.config.js"), projectDir) .then(({ compiler }) => { cleanupCompiler = compiler; }) @@ -39,7 +39,7 @@ describe("webpack loader", () => { it("should bundle using the first target w/ writeFile false w/o webpackTarget set", async () => { const target = resolve(projectDir, ".build", "lib", "main.js"); - const { compiler } = await createWebpackCompiler(requireWebpackConfig("webpack_noWebpackTarget.config.js"), projectDir); + const { compiler } = await webpackBuild(requireWebpackConfig("webpack_noWebpackTarget.config.js"), projectDir); cleanupCompiler = compiler; expect(statSync(target).isFile()).toBe(true); @@ -48,7 +48,7 @@ describe("webpack loader", () => { it("should select the writeFile target w/ webpackTarget set", async () => { const target = resolve(projectDir, ".build", "lib", "main.js"); - const { compiler } = await createWebpackCompiler(requireWebpackConfig("webpack_noNoWriteTargetsWithWebpackTarget.config.js"), projectDir); + const { compiler } = await webpackBuild(requireWebpackConfig("webpack_noNoWriteTargetsWithWebpackTarget.config.js"), projectDir); cleanupCompiler = compiler; expect(statSync(target).isFile()).toBe(true); @@ -57,7 +57,7 @@ describe("webpack loader", () => { it("should write a warning if no target w/ writeFile false is specified", async () => { const target = resolve(projectDir, ".build", "lib", "main.js"); - const { stats, compiler } = await createWebpackCompiler(requireWebpackConfig("webpack_noNoWriteTargets.config.js"), projectDir); + const { stats, compiler } = await webpackBuild(requireWebpackConfig("webpack_noNoWriteTargets.config.js"), projectDir); cleanupCompiler = compiler; expect(statSync(target).isFile()).toBe(true); @@ -67,7 +67,7 @@ describe("webpack loader", () => { it("should use default target w/o configured target and webpackTarget", async () => { const target = resolve(projectDir, ".build", "lib", "main.js"); - const { stats, compiler } = await createWebpackCompiler(requireWebpackConfig("webpack_noTargets.config.js"), projectDir); + const { stats, compiler } = await webpackBuild(requireWebpackConfig("webpack_noTargets.config.js"), projectDir); cleanupCompiler = compiler; expect(statSync(target).isFile()).toBe(true); @@ -77,7 +77,7 @@ describe("webpack loader", () => { it("should write a warning if more than one target w/ writeFile false is specified", async () => { const target = resolve(projectDir, ".build", "lib", "main.js"); - const { stats, compiler } = await createWebpackCompiler(requireWebpackConfig("webpack_multipleNoWriteTargets.config.js"), projectDir); + const { stats, compiler } = await webpackBuild(requireWebpackConfig("webpack_multipleNoWriteTargets.config.js"), projectDir); cleanupCompiler = compiler; expect(statSync(target).isFile()).toBe(true); @@ -88,7 +88,7 @@ describe("webpack loader", () => { it.skip("should bundle the file w/ thread-loader being used", async () => { const target = resolve(projectDir, ".build", "lib", "main.js"); - const { stats, compiler } = await createWebpackCompiler(requireWebpackConfig("webpack_thread_loader.config.js"), projectDir); + const { stats, compiler } = await webpackBuild(requireWebpackConfig("webpack_thread_loader.config.js"), projectDir); cleanupCompiler = compiler; expect(statSync(target).isFile()).toBe(true); @@ -101,7 +101,7 @@ describe("webpack loader", () => { it("should bundle invalid TypeScript file w/ transpileOnly being used", async () => { const target = resolve(projectDir, ".build", "lib", "main.js"); - const { stats, compiler } = await createWebpackCompiler(requireWebpackConfig("webpack_transpileOnly.config.js"), projectDir); + const { stats, compiler } = await webpackBuild(requireWebpackConfig("webpack_transpileOnly.config.js"), projectDir); cleanupCompiler = compiler; expect(statSync(target).isFile()).toBe(true); @@ -113,7 +113,7 @@ describe("webpack loader", () => { it("should bundle the file w/ fork-ts-checker-webpack-plugin being used", async () => { const target = resolve(projectDir, ".build", "lib", "main.js"); - const { stats, compiler } = await createWebpackCompiler(requireWebpackConfig("webpack_fork_ts.config.js"), projectDir); + const { stats, compiler } = await webpackBuild(requireWebpackConfig("webpack_fork_ts.config.js"), projectDir); cleanupCompiler = compiler; expect(statSync(target).isFile()).toBe(true); @@ -132,7 +132,7 @@ describe("webpack loader", () => { count++; }; - const { compiler } = await createWebpackCompiler(requireWebpackConfig("webpack.config.js", true), projectDir, callback); + const { compiler } = await webpackBuild(requireWebpackConfig("webpack.config.js", true), projectDir, callback); cleanupCompiler = compiler; let success = await waitFor(() => count > 0, "initial compile"); diff --git a/packages/webpack-test/src/webpack-utils.ts b/packages/webpack-test/src/webpack-utils.ts index 05f5fad4..608454e1 100644 --- a/packages/webpack-test/src/webpack-utils.ts +++ b/packages/webpack-test/src/webpack-utils.ts @@ -8,7 +8,7 @@ import { existsSync } from "fs"; import webpack, { Compiler, Configuration, Stats } from "webpack"; import { uPath } from "@quatico/websmith-webpack"; -export const createWebpackCompiler = ( +export const webpackBuild = ( options: Configuration, projectDir: string, callback?: (err?: Error | null, stats?: webpack.Stats) => void diff --git a/packages/webpack/src/TsCompiler.spec.ts b/packages/webpack/src/TsCompiler.spec.ts index 9b63a204..c136ba7b 100644 --- a/packages/webpack/src/TsCompiler.spec.ts +++ b/packages/webpack/src/TsCompiler.spec.ts @@ -55,7 +55,7 @@ describe("TsCompiler", () => { buildDir: resolve("./__TEMP__"), project: { declaration: true, target: 99, noEmitOnError: true }, reporter, - targets: [], + targets: ["*"], tsconfig: { options: {}, fileNames: [expected], errors: [] }, debug: true, sourceMap: false, @@ -115,7 +115,7 @@ describe("Transpilation", () => { buildDir: resolve("./__TEMP__"), project: { declaration: true, target: 99, noEmitOnError: true }, reporter, - targets: [], + targets: ["*"], tsconfig: { options: { declaration: true, target: 99 }, fileNames: [expected], errors: [] }, debug: true, sourceMap: false, diff --git a/packages/webpack/src/TsCompiler.ts b/packages/webpack/src/TsCompiler.ts index ed74a473..75047f61 100644 --- a/packages/webpack/src/TsCompiler.ts +++ b/packages/webpack/src/TsCompiler.ts @@ -5,7 +5,7 @@ * --------------------------------------------------------------------------------------------- */ -import { AddonRegistry, CompileFragment, Compiler, CompilerOptions } from "@quatico/websmith-core"; +import { AddonRegistry, CompilationConfig, CompileFragment, Compiler, CompilerOptions, resolveCompilationConfig } from "@quatico/websmith-core"; import ts from "typescript"; import { WebpackError } from "webpack"; import { Upath as uPath } from "./Upath"; @@ -18,30 +18,51 @@ export class TsCompiler extends Compiler { public webpackTarget: string; constructor(options: CompilerOptions, dependencyCallback: (filePath: string) => void, pluginOptions?: PluginOptions) { + const system = ts.sys; pluginOptions = pluginOptions ? { webpackTarget: "*", ...pluginOptions } : { config: "", webpackTarget: "*" }; + let websmithConfig: CompilationConfig = { + addons: + pluginOptions.addons + ?.split(",") + .map(it => it.trim()) + .filter(it => it.length > 0) ?? [], + addonsDir: pluginOptions.addonsDir, + ...(pluginOptions.transpileOnly ? { transpileOnly: pluginOptions.transpileOnly } : {}), + }; + if (pluginOptions.config) { + websmithConfig = { ...resolveCompilationConfig(pluginOptions.config, options.reporter, system), ...websmithConfig }; + } + + const { targets } = pluginOptions; + const { addons, targets: targetsMap, addonsDir } = websmithConfig ?? {}; + const targetNames = + targets + ?.split(",") + .map(it => it.trim()) + .filter(it => it.length > 0) ?? []; + const addonsMerged = addons?.length + ? addons + : Object.entries(targetsMap ?? {}) + .filter(([target]) => targetNames.includes(target)) + // eslint-disable-next-line @typescript-eslint/no-unused-vars + .map(([_, value]) => value.addons ?? []) + .flat(); super( options, - ts.sys, - options.config?.addonsDir ?? pluginOptions.addonsDir + system, + addonsMerged.length ? new AddonRegistry({ - addons: - options.config?.addons ?? - pluginOptions.addons - ?.split(",") - .map(it => it.trim()) - .filter(it => it.length > 0) ?? - [], - addonsDir: options.config?.addonsDir ?? pluginOptions.addonsDir ?? "./addons", + addons: addonsMerged, + addonsDir: addonsDir ?? options.config?.addonsDir ?? "./addons", reporter: options.reporter, - system: ts.sys, - }) + system, + }).refresh() : undefined, dependencyCallback ); this.pluginConfig = pluginOptions; - this.getAddonRegistry()?.refresh(); super.createTargetContextsIfNecessary(); - this.targets = options.targets; + this.targets = targetNames.length ? targetNames : options.targets; this.webpackTarget = this.getFragmentTarget(pluginOptions.webpackTarget!); } diff --git a/packages/webpack/src/instance-cache.spec.ts b/packages/webpack/src/instance-cache.spec.ts index 5ec48a20..87f19541 100644 --- a/packages/webpack/src/instance-cache.spec.ts +++ b/packages/webpack/src/instance-cache.spec.ts @@ -125,6 +125,6 @@ describe("getCacheName", () => { const actual = getCacheName(context); - expect(actual).toBe(expected); + expect(actual).toContain(expected); }); }); diff --git a/packages/webpack/src/instance-cache.ts b/packages/webpack/src/instance-cache.ts index be142ab7..24c36d34 100644 --- a/packages/webpack/src/instance-cache.ts +++ b/packages/webpack/src/instance-cache.ts @@ -51,4 +51,13 @@ export const initializeInstance = ( return instance; }; -export const getCacheName = (loader: webpack.LoaderContext) => `websmith-${loader._compilation?.hash ?? ""}`; +export const getCacheName = (loader: webpack.LoaderContext) => { + if (loader._compilation && !loader._compilation.hash) { + loader._compilation.hash = generateRandomString(); + } + return `websmith-${loader._compilation?.hash}`; +}; + +const generateRandomString = () => { + return Math.floor(Math.random() * Date.now()).toString(36); +}; diff --git a/packages/webpack/src/loader-options.ts b/packages/webpack/src/loader-options.ts index 08d04f25..706c163a 100644 --- a/packages/webpack/src/loader-options.ts +++ b/packages/webpack/src/loader-options.ts @@ -13,7 +13,7 @@ export interface PluginArguments { addons?: string; addonsDir?: string; buildDir?: string; - config: string; + config?: string; debug?: boolean; project?: string; sourceMap?: boolean; diff --git a/packages/webpack/src/options.ts b/packages/webpack/src/options.ts index 8e8705c3..0be5c136 100644 --- a/packages/webpack/src/options.ts +++ b/packages/webpack/src/options.ts @@ -32,7 +32,7 @@ export const createOptions = (args: Partial, reporter: Reporter = const compilationConfig = resolveCompilationConfig(args.config ?? DEFAULTS.config, reporter, system); const projectDirectory = - (compilationConfig && dirname(compilationConfig.configFilePath)) ?? + (compilationConfig?.configFilePath && dirname(compilationConfig.configFilePath)) ?? (tsconfig.raw && tsconfig.raw.configFilePath && dirname(tsconfig.raw?.configFilePath)); tsconfig.options.outDir = args.buildDir ?? tsconfig.options.outDir ?? DEFAULTS.outDir; if (projectDirectory) { diff --git a/packages/webpack/src/webpack-hooks.ts b/packages/webpack/src/webpack-hooks.ts index 4209ec4a..faac143f 100644 --- a/packages/webpack/src/webpack-hooks.ts +++ b/packages/webpack/src/webpack-hooks.ts @@ -29,7 +29,9 @@ export const addCompilationHooks = (compiler: Compiler, options: PluginOptions, initializeInstance(context, options, dependencyCallback); const instance = getInstanceFromCache(compilation.compiler, context) ?? new TsCompiler(createOptions(options), dependencyCallback, options); - instance.pluginConfig = JSON.parse(readFileSync(options.config).toString()); + if (options.config) { + instance.pluginConfig = JSON.parse(readFileSync(options.config).toString()); + } setInstanceInCache(compilation.compiler, context, instance); } });