From ff0e8cc54e5e68631cd83302d59b19f2626d43cb Mon Sep 17 00:00:00 2001 From: Alice Pote Date: Mon, 2 May 2022 17:52:26 -0400 Subject: [PATCH] feat(compiler): export all built components from index.js w/ dist-custom-elements This makes a change to the `dist-custom-elements` output target code which adds exports from the generated `index.js` file for all of the components included in the build. This should help to enable Stencil users to migrate away from the `dist-custom-elements-bundle` output target, which we're planning to remove in the future (see #3136 for details and discussion on that). In order to enable this we don't need to make a particularly large change. The index chunk which we generate and pass to Rollup is just amended to include some references to the other modules we declare (one per each component), and Rollup takes care of resolving that into actual, bundled concrete modules. As part of making this change there is also a new test file added to exercise the functions for the `dist-custom-elements` output target, which necessitated improving our mocks a little bit. This should help us to test the rest of the output target code in the future. STENCIL-332 Investigate re-exporting components via index.js --- jest.config.js | 1 + src/compiler/bundle/bundle-interface.ts | 22 +++ src/compiler/bundle/loader-plugin.ts | 1 + .../dist-custom-elements/index.ts | 137 ++++++++++---- ...utput-targets-dist-custom-elements.spec.ts | 175 ++++++++++++++++++ .../update-stencil-core-import.ts | 3 + src/testing/index.ts | 1 + src/testing/mocks.ts | 57 +++++- src/testing/testing-sys.ts | 2 + 9 files changed, 364 insertions(+), 35 deletions(-) create mode 100644 src/compiler/output-targets/test/output-targets-dist-custom-elements.spec.ts diff --git a/jest.config.js b/jest.config.js index 2071bc01ade..d09c39fe6cc 100644 --- a/jest.config.js +++ b/jest.config.js @@ -3,6 +3,7 @@ module.exports = { moduleNameMapper: { '@app-data': '/internal/app-data/index.cjs', '@app-globals': '/internal/app-globals/index.cjs', + '@compiler-deps': '/src/compiler/sys/modules/compiler-deps.ts', '@platform': '/internal/testing/index.js', '@runtime': '/internal/testing/index.js', '@stencil/core/cli': '/cli/index.js', diff --git a/src/compiler/bundle/bundle-interface.ts b/src/compiler/bundle/bundle-interface.ts index 81d82ea472d..d633254642c 100644 --- a/src/compiler/bundle/bundle-interface.ts +++ b/src/compiler/bundle/bundle-interface.ts @@ -8,9 +8,31 @@ export interface BundleOptions { externalRuntime?: boolean; platform: 'client' | 'hydrate' | 'worker'; customTransformers?: TransformerFactory[]; + /** + * This is equivalent to the Rollup `input` configuration option. It's + * an object mapping names to entry points which tells Rollup to bundle + * each thing up as a separate output chunk. + * + * @see {@link https://rollupjs.org/guide/en/#input} + */ inputs: { [entryKey: string]: string }; + /** + * A map of strings which are passed to the Stencil-specific loader plugin + * which we use to resolve the imports of Stencil project files when building + * with Rollup. + * + * @see {@link loader-plugin:loaderPlugin} + */ loader?: { [id: string]: string }; inlineDynamicImports?: boolean; inlineWorkers?: boolean; + /** + * Duplicate of Rollup's `preserveEntrySignatures` option. + * + * "Controls if Rollup tries to ensure that entry chunks have the same + * exports as the underlying entry module." + * + * @see {@link https://rollupjs.org/guide/en/#preserveentrysignatures} + */ preserveEntrySignatures?: PreserveEntrySignaturesOption; } diff --git a/src/compiler/bundle/loader-plugin.ts b/src/compiler/bundle/loader-plugin.ts index 2836ab78853..091cc702ebe 100644 --- a/src/compiler/bundle/loader-plugin.ts +++ b/src/compiler/bundle/loader-plugin.ts @@ -5,6 +5,7 @@ import type { LoadResult, Plugin, ResolveIdResult } from 'rollup'; * using the `dist-custom-elements` output target may have a single 'entry point' for each file containing a component. * Each of those files will be independently resolved and loaded by this plugin for further processing by Rollup later * in the bundling process. + * * @param entries the Stencil project files to process. It should be noted that the keys in this object may not * necessarily be an absolute or relative path to a file, but may be a Rollup Virtual Module (which begin with \0). * @returns the rollup plugin that loads and process a Stencil project's entry points diff --git a/src/compiler/output-targets/dist-custom-elements/index.ts b/src/compiler/output-targets/dist-custom-elements/index.ts index ce44e9763d4..8af9d641bbc 100644 --- a/src/compiler/output-targets/dist-custom-elements/index.ts +++ b/src/compiler/output-targets/dist-custom-elements/index.ts @@ -7,6 +7,7 @@ import { generatePreamble, getSourceMappingUrlForEndOfFile, hasError, + isString, rollupToStencilSourceMap, } from '@utils'; import { getCustomElementsBuildConditionals } from '../dist-custom-elements-bundle/custom-elements-build-conditionals'; @@ -21,6 +22,17 @@ import { proxyCustomElement } from '../../transformers/component-native/proxy-cu import { updateStencilCoreImports } from '../../transformers/update-stencil-core-import'; import ts from 'typescript'; +/** + * Main output target function for `dist-custom-elements`. This function just + * does some organizational work to call the other functions in this module, + * which do actual work of generating the rollup configuration, creating an + * entry chunk, running, the build, etc. + * + * @param config the user-supplied compiler configuration we're using + * @param compilerCtx the current compiler context + * @param buildCtx the current build context + * @returns an empty Promise which won't resolve until the work is done! + */ export const outputCustomElements = async ( config: d.Config, compilerCtx: d.CompilerCtx, @@ -30,7 +42,7 @@ export const outputCustomElements = async ( return; } - const outputTargets = config.outputTargets.filter(isOutputTargetDistCustomElements); + const outputTargets = (config.outputTargets ?? []).filter(isOutputTargetDistCustomElements); if (outputTargets.length === 0) { return; } @@ -38,38 +50,72 @@ export const outputCustomElements = async ( const bundlingEventMessage = 'generate custom elements'; const timespan = buildCtx.createTimeSpan(`${bundlingEventMessage} started`); - await Promise.all(outputTargets.map((o) => bundleCustomElements(config, compilerCtx, buildCtx, o))); + await Promise.all(outputTargets.map((target) => bundleCustomElements(config, compilerCtx, buildCtx, target))); timespan.finish(`${bundlingEventMessage} finished`); }; -const bundleCustomElements = async ( +/** + * Get bundle options for our current build and compiler context which we'll use + * to generate a Rollup build and so on. + * + * @param config user-supplied Stencil configuration + * @param buildCtx the current build context + * @param compilerCtx the current compiler context + * @param outputTarget the outputTarget we're currently dealing with + * @returns bundle options suitable for generating a rollup configuration + */ +export const getBundleOptions = ( + config: d.Config, + buildCtx: d.BuildCtx, + compilerCtx: d.CompilerCtx, + outputTarget: d.OutputTargetDistCustomElements +): BundleOptions => ({ + id: 'customElements', + platform: 'client', + conditionals: getCustomElementsBuildConditionals(config, buildCtx.components), + customTransformers: getCustomElementCustomTransformer(config, compilerCtx, buildCtx.components, outputTarget), + externalRuntime: !!outputTarget.externalRuntime, + inlineWorkers: true, + inputs: { + // Here we prefix our index chunk with '\0' to tell Rollup that we're + // going to be using virtual modules with this module. A leading '\0' + // prevents other plugins from messing with the module. We generate a + // string for the index chunk below in the `loader` property. + // + // @see {@link https://rollupjs.org/guide/en/#conventions} for more info. + index: '\0core', + }, + loader: { + '\0core': generateEntryPoint(outputTarget), + }, + inlineDynamicImports: outputTarget.inlineDynamicImports, + preserveEntrySignatures: 'allow-extension', +}); + +/** + * Get bundle options for rollup, run the rollup build, optionally minify the + * output, and write files to disk. + * @param config user-supplied Stencil configuration + * @param buildCtx the current build context + * @param compilerCtx the current compiler context + * @param outputTarget the outputTarget we're currently dealing with + * @returns an empty promise + */ + +export const bundleCustomElements = async ( config: d.Config, compilerCtx: d.CompilerCtx, buildCtx: d.BuildCtx, outputTarget: d.OutputTargetDistCustomElements ) => { try { - const bundleOpts: BundleOptions = { - id: 'customElements', - platform: 'client', - conditionals: getCustomElementsBuildConditionals(config, buildCtx.components), - customTransformers: getCustomElementCustomTransformer(config, compilerCtx, buildCtx.components, outputTarget), - externalRuntime: !!outputTarget.externalRuntime, - inlineWorkers: true, - inputs: { - index: '\0core', - }, - loader: { - '\0core': generateEntryPoint(outputTarget), - }, - inlineDynamicImports: outputTarget.inlineDynamicImports, - preserveEntrySignatures: 'allow-extension', - }; + const bundleOpts = getBundleOptions(config, buildCtx, compilerCtx, outputTarget); addCustomElementInputs(buildCtx, bundleOpts); const build = await bundleOutput(config, compilerCtx, buildCtx, bundleOpts); + if (build) { const rollupOutput = await build.generate({ banner: generatePreamble(config), @@ -81,6 +127,20 @@ const bundleCustomElements = async ( preferConst: true, }); + // the output target should have been validated at this point - as a result, we expect this field + // to have been backfilled if it wasn't provided + const outputTargetDir: string = outputTarget.dir!; + + // besides, if it isn't here we do a diagnostic and an early return + if (!isString(outputTargetDir)) { + buildCtx.diagnostics.push({ + level: 'error', + type: 'build', + messageText: 'dist-custom-elements output target provided with no output target directory!', + }); + return; + } + const minify = outputTarget.externalRuntime || outputTarget.minify !== true ? false : config.minifyJs; const files = rollupOutput.output.map(async (bundle) => { if (bundle.type === 'chunk') { @@ -96,19 +156,15 @@ const bundleCustomElements = async ( buildCtx.diagnostics.push(...optimizeResults.diagnostics); if (!hasError(optimizeResults.diagnostics) && typeof optimizeResults.output === 'string') { code = optimizeResults.output; - sourceMap = optimizeResults.sourceMap; } - if (sourceMap) { + if (optimizeResults.sourceMap) { + sourceMap = optimizeResults.sourceMap; code = code + getSourceMappingUrlForEndOfFile(bundle.fileName); - await compilerCtx.fs.writeFile( - join(outputTarget.dir, bundle.fileName + '.map'), - JSON.stringify(sourceMap), - { - outputTargetType: outputTarget.type, - } - ); + await compilerCtx.fs.writeFile(join(outputTargetDir, bundle.fileName + '.map'), JSON.stringify(sourceMap), { + outputTargetType: outputTarget.type, + }); } - await compilerCtx.fs.writeFile(join(outputTarget.dir, bundle.fileName), code, { + await compilerCtx.fs.writeFile(join(outputTargetDir, bundle.fileName), code, { outputTargetType: outputTarget.type, }); } @@ -125,8 +181,11 @@ const bundleCustomElements = async ( * @param buildCtx the context for the current build * @param bundleOpts the bundle options to store the virtual modules under. acts as an output parameter */ -const addCustomElementInputs = (buildCtx: d.BuildCtx, bundleOpts: BundleOptions): void => { +export const addCustomElementInputs = (buildCtx: d.BuildCtx, bundleOpts: BundleOptions): void => { const components = buildCtx.components; + // an array to store the imports of these modules that we're going to add to our entry chunk + const indexImports: string[] = []; + components.forEach((cmp) => { const exp: string[] = []; const exportName = dashToPascalCase(cmp.tagName); @@ -136,6 +195,7 @@ const addCustomElementInputs = (buildCtx: d.BuildCtx, bundleOpts: BundleOptions) if (cmp.isPlain) { exp.push(`export { ${importName} as ${exportName} } from '${cmp.sourceFilePath}';`); + indexImports.push(`export { {${exportName} } from '${coreKey}';`); } else { // the `importName` may collide with the `exportName`, alias it just in case it does with `importAs` exp.push( @@ -143,11 +203,23 @@ const addCustomElementInputs = (buildCtx: d.BuildCtx, bundleOpts: BundleOptions) ); exp.push(`export const ${exportName} = ${importAs};`); exp.push(`export const defineCustomElement = cmpDefCustomEle;`); + + // Here we push an export (with a rename for `defineCustomElement` for + // this component onto our array which references the `coreKey` (prefixed + // with `\0`). We have to do this so that our import is referencing the + // correct virtual module, if we instead referenced, for instance, + // `cmp.sourceFilePath`, we would end up with duplicated modules in our + // output. + indexImports.push( + `export { ${exportName}, defineCustomElement as defineCustomElement${exportName} } from '${coreKey}';` + ); } bundleOpts.inputs[cmp.tagName] = coreKey; - bundleOpts.loader[coreKey] = exp.join('\n'); + bundleOpts.loader![coreKey] = exp.join('\n'); }); + + bundleOpts.loader!['\0core'] += indexImports.join('\n'); }; /** @@ -155,7 +227,7 @@ const addCustomElementInputs = (buildCtx: d.BuildCtx, bundleOpts: BundleOptions) * @param outputTarget the output target's configuration * @returns the stringified contents to be placed in the entrypoint */ -const generateEntryPoint = (outputTarget: d.OutputTargetDistCustomElements): string => { +export const generateEntryPoint = (outputTarget: d.OutputTargetDistCustomElements): string => { const imp: string[] = []; imp.push( @@ -173,6 +245,7 @@ const generateEntryPoint = (outputTarget: d.OutputTargetDistCustomElements): str /** * Get the series of custom transformers that will be applied to a Stencil project's source code during the TypeScript * transpilation process + * * @param config the configuration for the Stencil project * @param compilerCtx the current compiler context * @param components the components that will be compiled as a part of the current build diff --git a/src/compiler/output-targets/test/output-targets-dist-custom-elements.spec.ts b/src/compiler/output-targets/test/output-targets-dist-custom-elements.spec.ts new file mode 100644 index 00000000000..1bef50d5ce4 --- /dev/null +++ b/src/compiler/output-targets/test/output-targets-dist-custom-elements.spec.ts @@ -0,0 +1,175 @@ +import { path } from '@stencil/core/compiler'; +import { mockConfig, mockStencilSystem, mockBuildCtx, mockCompilerCtx, mockModule } from '@stencil/core/testing'; +import type * as d from '../../../declarations'; +import { + addCustomElementInputs, + bundleCustomElements, + generateEntryPoint, + getBundleOptions, + outputCustomElements, +} from '../dist-custom-elements'; +import * as outputCustomElementsMod from '../dist-custom-elements'; +import { OutputTargetDistCustomElements } from '../../../declarations'; +import { stubComponentCompilerMeta } from '../../types/tests/ComponentCompilerMeta.stub'; +import { STENCIL_APP_GLOBALS_ID, STENCIL_INTERNAL_CLIENT_ID, USER_INDEX_ENTRY_ID } from '../../bundle/entry-alias-ids'; +import { DIST_CUSTOM_ELEMENTS, DIST_CUSTOM_ELEMENTS_BUNDLE } from '../output-utils'; + +const setup = () => { + const sys = mockStencilSystem(); + const config: d.Config = mockConfig(sys); + const compilerCtx = mockCompilerCtx(config); + const buildCtx = mockBuildCtx(config, compilerCtx); + const root = config.rootDir; + config.configPath = '/testing-path'; + config.srcDir = '/src'; + config.buildAppCore = true; + config.rootDir = path.join(root, 'User', 'testing', '/'); + config.namespace = 'TestApp'; + config.buildEs5 = true; + config.globalScript = path.join(root, 'User', 'testing', 'src', 'global.ts'); + config.outputTargets = [{ type: DIST_CUSTOM_ELEMENTS }]; + + const bundleCustomElementsSpy = jest.spyOn(outputCustomElementsMod, 'bundleCustomElements'); + + compilerCtx.moduleMap.set('test', mockModule()); + + return { config, compilerCtx, buildCtx, bundleCustomElementsSpy }; +}; + +describe('Custom Elements output target', () => { + it('should return early if config.buildDist is false', async () => { + const { config, compilerCtx, buildCtx, bundleCustomElementsSpy } = setup(); + config.buildDist = false; + await outputCustomElements(config, compilerCtx, buildCtx); + expect(bundleCustomElementsSpy).not.toHaveBeenCalled(); + }); + + it.each([ + [[]], + [[{ type: 'dist' }]], + [[{ type: 'dist' }, { type: DIST_CUSTOM_ELEMENTS_BUNDLE }]], + ])('should return early if no appropriate output target (%j)', async (outputTargets) => { + const { config, compilerCtx, buildCtx, bundleCustomElementsSpy } = setup(); + config.outputTargets = outputTargets; + await outputCustomElements(config, compilerCtx, buildCtx); + expect(bundleCustomElementsSpy).not.toHaveBeenCalled(); + }); + + describe('generateEntryPoint', () => { + it.each([true, false])('should include globalScripts if the right option is set', (includeGlobalScripts) => { + const entryPoint = generateEntryPoint({ + type: DIST_CUSTOM_ELEMENTS, + includeGlobalScripts, + }); + const globalScriptsBoilerplate = `import { globalScripts } from '${STENCIL_APP_GLOBALS_ID}';\nglobalScripts();`; + expect(entryPoint.includes(globalScriptsBoilerplate)).toBe(includeGlobalScripts); + }); + }); + + describe('getBundleOptions', () => { + it('should set basic properties on BundleOptions', () => { + const { config, buildCtx, compilerCtx } = setup(); + const options = getBundleOptions(config, buildCtx, compilerCtx, { type: DIST_CUSTOM_ELEMENTS }); + expect(options.id).toBe('customElements'); + expect(options.platform).toBe('client'); + expect(options.inlineWorkers).toBe(true); + expect(options.inputs).toEqual({ + index: '\0core', + }); + expect(options.loader).toEqual({ + '\0core': generateEntryPoint({ type: DIST_CUSTOM_ELEMENTS }), + }); + expect(options.preserveEntrySignatures).toEqual('allow-extension'); + }); + + it.each([true, false, undefined])('should set externalRuntime correctly when %p', (externalRuntime) => { + const { config, buildCtx, compilerCtx } = setup(); + const options = getBundleOptions(config, buildCtx, compilerCtx, { + type: DIST_CUSTOM_ELEMENTS, + externalRuntime, + }); + if (externalRuntime) { + expect(options.externalRuntime).toBe(true); + } else { + expect(options.externalRuntime).toBe(false); + } + }); + + it.each([true, false, undefined])('should pass through inlineDynamicImports=%p', (inlineDynamicImports) => { + const { config, buildCtx, compilerCtx } = setup(); + const options = getBundleOptions(config, buildCtx, compilerCtx, { + type: DIST_CUSTOM_ELEMENTS, + inlineDynamicImports, + }); + expect(options.inlineDynamicImports).toBe(inlineDynamicImports); + }); + }); + + describe('bundleCustomElements', () => { + it('should set a diagnostic if no `dir` prop on the output target', async () => { + const { config, compilerCtx, buildCtx } = setup(); + const outputTarget: OutputTargetDistCustomElements = { type: DIST_CUSTOM_ELEMENTS }; + await bundleCustomElements(config, compilerCtx, buildCtx, outputTarget); + expect(buildCtx.diagnostics).toEqual([ + { + level: 'error', + type: 'build', + messageText: 'dist-custom-elements output target provided with no output target directory!', + }, + ]); + }); + }); + + describe('addCustomElementInputs', () => { + it('should add imports to index.js for all included components', () => { + const componentOne = stubComponentCompilerMeta(); + const componentTwo = stubComponentCompilerMeta({ + componentClassName: 'MyBestComponent', + tagName: 'my-best-component', + }); + const { config, compilerCtx, buildCtx } = setup(); + buildCtx.components = [componentOne, componentTwo]; + + const bundleOptions = getBundleOptions( + config, + buildCtx, + compilerCtx, + config.outputTargets[0] as OutputTargetDistCustomElements + ); + addCustomElementInputs(buildCtx, bundleOptions); + expect(bundleOptions.loader['\0core']).toEqual( + `export { setAssetPath, setPlatformOptions } from '${STENCIL_INTERNAL_CLIENT_ID}'; +export * from '${USER_INDEX_ENTRY_ID}'; +import { globalScripts } from '${STENCIL_APP_GLOBALS_ID}'; +globalScripts(); +export { StubCmp, defineCustomElement as defineCustomElementStubCmp } from '\0StubCmp'; +export { MyBestComponent, defineCustomElement as defineCustomElementMyBestComponent } from '\0MyBestComponent';` + ); + }); + + it('should correctly handle capitalization edge-cases', () => { + const component = stubComponentCompilerMeta({ + componentClassName: 'ComponentWithJSX', + tagName: 'component-with-jsx', + }); + + const { config, compilerCtx, buildCtx } = setup(); + buildCtx.components = [component]; + + const bundleOptions = getBundleOptions( + config, + buildCtx, + compilerCtx, + config.outputTargets[0] as OutputTargetDistCustomElements + ); + addCustomElementInputs(buildCtx, bundleOptions); + expect(bundleOptions.loader['\0core']).toEqual( + `export { setAssetPath, setPlatformOptions } from '${STENCIL_INTERNAL_CLIENT_ID}'; +export * from '${USER_INDEX_ENTRY_ID}'; +import { globalScripts } from '${STENCIL_APP_GLOBALS_ID}'; +globalScripts(); +export { ComponentWithJsx, defineCustomElement as defineCustomElementComponentWithJsx } from '\0ComponentWithJsx';` + ); + }); + }); +}); diff --git a/src/compiler/transformers/update-stencil-core-import.ts b/src/compiler/transformers/update-stencil-core-import.ts index 20731a37dbd..3a1a218e56a 100644 --- a/src/compiler/transformers/update-stencil-core-import.ts +++ b/src/compiler/transformers/update-stencil-core-import.ts @@ -69,6 +69,9 @@ export const updateStencilCoreImports = (updatedCoreImportPath: string): ts.Tran }; }; +/** + * A set of imports which we don't want to remove from an output file + */ const KEEP_IMPORTS = new Set([ 'h', 'setMode', diff --git a/src/testing/index.ts b/src/testing/index.ts index b7c30a4f273..f8b54f7a4ca 100644 --- a/src/testing/index.ts +++ b/src/testing/index.ts @@ -11,6 +11,7 @@ export { mockLogger, mockStencilSystem, mockWindow, + mockModule, } from './mocks'; export { MockHeaders, diff --git a/src/testing/mocks.ts b/src/testing/mocks.ts index 197d5897dfe..02f9f2fe947 100644 --- a/src/testing/mocks.ts +++ b/src/testing/mocks.ts @@ -1,4 +1,4 @@ -import type { BuildCtx, Cache, CompilerCtx, CompilerSystem, Config } from '@stencil/core/internal'; +import type { BuildCtx, Cache, CompilerCtx, CompilerSystem, Config, Module } from '@stencil/core/internal'; import { BuildContext } from '../compiler/build/build-ctx'; import { Cache as CompilerCache } from '../compiler/cache'; import { createInMemoryFs } from '../compiler/sys/in-memory-fs'; @@ -8,6 +8,7 @@ import { MockWindow } from '@stencil/core/mock-doc'; import { TestingLogger } from './testing-logger'; import path from 'path'; import { noop } from '@utils'; +import { buildEvents } from '../compiler/events'; export function mockConfig(sys?: CompilerSystem) { const rootDir = path.resolve('/'); @@ -44,6 +45,10 @@ export function mockConfig(sys?: CompilerSystem) { customResolveOptions: {}, }, sourceMap: true, + rollupPlugins: { + before: [], + after: [], + }, }; return config; @@ -70,7 +75,7 @@ export function mockCompilerCtx(config?: Config) { compilerOptions: null, cache: null, cssModuleImports: new Map(), - events: null, + events: buildEvents(), fs: null, hasSuccessfulBuild: false, isActivelyBuilding: false, @@ -108,7 +113,7 @@ export function mockCompilerCtx(config?: Config) { return compilerCtx; } -export function mockBuildCtx(config?: Config, compilerCtx?: CompilerCtx) { +export function mockBuildCtx(config?: Config, compilerCtx?: CompilerCtx): BuildCtx { if (!config) { config = mockConfig(); } @@ -150,3 +155,49 @@ export function mockWindow(html: string = null) { const win = new MockWindow(html); return win as any as Window; } + +/** + * This gives you a mock Module, an interface which is the internal compiler + * representation of a module. It includes a bunch of information necessary for + * compilation, this mock basically sets sane defaults for all those values. + * + * @param mod is an override module that you can supply to set particular values + * @returns a module object ready to use in tests! + */ +export const mockModule = (mod: Partial = {}): Module => ({ + cmps: [], + coreRuntimeApis: [], + collectionName: '', + dtsFilePath: '', + excludeFromCollection: false, + externalImports: [], + htmlAttrNames: [], + htmlTagNames: [], + htmlParts: [], + isCollectionDependency: false, + isLegacy: false, + jsFilePath: '', + localImports: [], + originalImports: [], + originalCollectionComponentPath: '', + potentialCmpRefs: [], + sourceFilePath: '', + staticSourceFile: '', + staticSourceFileText: '', + sourceMapPath: '', + sourceMapFileText: '', + + // build features + hasVdomAttribute: false, + hasVdomClass: false, + hasVdomFunctional: false, + hasVdomKey: false, + hasVdomListener: false, + hasVdomPropOrAttr: false, + hasVdomRef: false, + hasVdomRender: false, + hasVdomStyle: false, + hasVdomText: false, + hasVdomXlink: false, + ...mod, +}); diff --git a/src/testing/testing-sys.ts b/src/testing/testing-sys.ts index 0ea34d132f9..622b4863245 100644 --- a/src/testing/testing-sys.ts +++ b/src/testing/testing-sys.ts @@ -62,6 +62,8 @@ export const createTestingSystem = (): TestingSystem => { sys.writeFile = wrapWrite(sys.writeFile); sys.writeFileSync = wrapWrite(sys.writeFileSync); + sys.getCompilerExecutingPath = () => 'bin/stencil.js'; + Object.defineProperties(sys, { diskReads: { get() {