Skip to content

Commit

Permalink
feat(dist-custom-elements): automatically import and define dependenc…
Browse files Browse the repository at this point in the history
…ies (#3039)

add the ability to register all dependent dependencies for the `dist-custom-elements`
output target. this allows users the opportunity to not manually register child 
components when using this output target.

add `defineCustomElement` to every component and export it. when invoked, 
this function - will bootstrap components and any dependents

add `autoDefineCustomElement` flag to output target to make new behavior
opt-in

extract transformer logic into separate files

add custom-element testing infrastructure, tests to support the aforementioned
functionality
  • Loading branch information
johnjenkins authored Sep 29, 2021
1 parent 037b228 commit 6987e43
Show file tree
Hide file tree
Showing 26 changed files with 607 additions and 87 deletions.
4 changes: 2 additions & 2 deletions src/compiler/bundle/app-data-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,12 +148,12 @@ const appendGlobalScripts = (globalScripts: GlobalScript[], s: MagicString) => {
};

const appendBuildConditionals = (config: d.Config, build: d.BuildConditionals, s: MagicString) => {
const builData = Object.keys(build)
const buildData = Object.keys(build)
.sort()
.map((key) => key + ': ' + ((build as any)[key] ? 'true' : 'false'))
.join(', ');

s.append(`export const BUILD = /* ${config.fsNamespace} */ { ${builData} };\n`);
s.append(`export const BUILD = /* ${config.fsNamespace} */ { ${buildData} };\n`);
};

const appendEnv = (config: d.Config, s: MagicString) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@ const generateCustomElementType = (componentsDtsRelPath: string, cmp: d.Componen
` prototype: ${tagNameAsPascal};`,
` new (): ${tagNameAsPascal};`,
`};`,
`/**`,
` * Used to define this component and all nested components recursively.`,
` */`,
`export const defineCustomElement: () => void;`,
``,
];

Expand Down
29 changes: 21 additions & 8 deletions src/compiler/output-targets/dist-custom-elements/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import type * as d from '../../../declarations';
import type { BundleOptions } from '../../bundle/bundle-interface';
import { bundleOutput } from '../../bundle/bundle-output';
import { catchError, dashToPascalCase, formatComponentRuntimeMeta, hasError, stringifyRuntimeData } from '@utils';
import { catchError, dashToPascalCase, hasError } from '@utils';
import { getCustomElementsBuildConditionals } from '../dist-custom-elements-bundle/custom-elements-build-conditionals';
import { isOutputTargetDistCustomElements } from '../output-utils';
import { join } from 'path';
import { nativeComponentTransform } from '../../transformers/component-native/tranform-to-native-component';
import { addDefineCustomElementFunctions } from '../../transformers/component-native/add-define-custom-element-function';
import { optimizeModule } from '../../optimize/optimize-module';
import { removeCollectionImports } from '../../transformers/remove-collection-imports';
import { STENCIL_INTERNAL_CLIENT_ID, USER_INDEX_ENTRY_ID, STENCIL_APP_GLOBALS_ID } from '../../bundle/entry-alias-ids';
Expand Down Expand Up @@ -39,7 +40,12 @@ const bundleCustomElements = async (
id: 'customElements',
platform: 'client',
conditionals: getCustomElementsBuildConditionals(config, buildCtx.components),
customTransformers: getCustomElementBundleCustomTransformer(config, compilerCtx),
customTransformers: getCustomElementBundleCustomTransformer(
config,
compilerCtx,
buildCtx.components,
outputTarget
),
externalRuntime: !!outputTarget.externalRuntime,
inlineWorkers: true,
inputs: {
Expand Down Expand Up @@ -69,6 +75,7 @@ const bundleCustomElements = async (
const files = rollupOutput.output.map(async (bundle) => {
if (bundle.type === 'chunk') {
let code = bundle.code;

const optimizeResults = await optimizeModule(config, compilerCtx, {
input: code,
isCore: bundle.isEntry,
Expand Down Expand Up @@ -106,11 +113,11 @@ const addCustomElementInputs = (
if (cmp.isPlain) {
exp.push(`export { ${importName} as ${exportName} } from '${cmp.sourceFilePath}';`);
} else {
const meta = stringifyRuntimeData(formatComponentRuntimeMeta(cmp, false));

exp.push(`import { proxyCustomElement } from '${STENCIL_INTERNAL_CLIENT_ID}';`);
exp.push(`import { ${importName} as ${importAs} } from '${cmp.sourceFilePath}';`);
exp.push(`export const ${exportName} = /*@__PURE__*/proxyCustomElement(${importAs}, ${meta});`);
exp.push(
`import { ${importName} as ${importAs}, defineCustomElement as cmpDefCustomEle } from '${cmp.sourceFilePath}';`
);
exp.push(`export const ${exportName} = ${importAs};`);
exp.push(`export const defineCustomElement = cmpDefCustomEle;`);
}

bundleOpts.inputs[cmp.tagName] = coreKey;
Expand All @@ -134,7 +141,12 @@ const generateEntryPoint = (outputTarget: d.OutputTargetDistCustomElements, _bui
return [...imp, ...exp].join('\n') + '\n';
};

const getCustomElementBundleCustomTransformer = (config: d.Config, compilerCtx: d.CompilerCtx) => {
const getCustomElementBundleCustomTransformer = (
config: d.Config,
compilerCtx: d.CompilerCtx,
components: d.ComponentCompilerMeta[],
outputTarget: d.OutputTargetDistCustomElements
) => {
const transformOpts: d.TransformOptions = {
coreImportPath: STENCIL_INTERNAL_CLIENT_ID,
componentExport: null,
Expand All @@ -145,6 +157,7 @@ const getCustomElementBundleCustomTransformer = (config: d.Config, compilerCtx:
styleImportData: 'queryparams',
};
return [
addDefineCustomElementFunctions(compilerCtx, components, outputTarget),
updateStencilCoreImports(transformOpts.coreImportPath),
nativeComponentTransform(compilerCtx, transformOpts),
removeCollectionImports(compilerCtx),
Expand Down
12 changes: 7 additions & 5 deletions src/compiler/transformers/add-component-meta-proxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@ export const addModuleMetadataProxies = (tsSourceFile: ts.SourceFile, moduleFile
};

const addComponentMetadataProxy = (compilerMeta: d.ComponentCompilerMeta) => {
return ts.createStatement(createComponentMetadataProxy(compilerMeta));
};

export const createComponentMetadataProxy = (compilerMeta: d.ComponentCompilerMeta) => {
const compactMeta: d.ComponentRuntimeMetaCompact = formatComponentRuntimeMeta(compilerMeta, true);

const liternalCmpClassName = ts.createIdentifier(compilerMeta.componentClassName);
const liternalMeta = convertValueToLiteral(compactMeta);
const literalCmpClassName = ts.createIdentifier(compilerMeta.componentClassName);
const literalMeta = convertValueToLiteral(compactMeta);

return ts.createStatement(
ts.createCall(ts.createIdentifier(PROXY_CUSTOM_ELEMENT), [], [liternalCmpClassName, liternalMeta])
);
return ts.createCall(ts.createIdentifier(PROXY_CUSTOM_ELEMENT), [], [literalCmpClassName, literalMeta]);
};
75 changes: 6 additions & 69 deletions src/compiler/transformers/add-imports.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type * as d from '../../declarations';
import ts from 'typescript';
import { createRequireStatement, createImportStatement } from './transform-utils';

export const addImports = (
transformOpts: d.TransformOptions,
Expand All @@ -13,79 +14,15 @@ export const addImports = (

if (transformOpts.module === 'cjs') {
// CommonJS require()
return addCjsRequires(tsSourceFile, importFnNames, importPath);
const newRequire = createRequireStatement(importFnNames, importPath);
const statements = tsSourceFile.statements.slice();
statements.splice(2, 0, newRequire);
return ts.updateSourceFileNode(tsSourceFile, statements);
}

// ESM Imports
return addEsmImports(tsSourceFile, importFnNames, importPath);
};

const addEsmImports = (tsSourceFile: ts.SourceFile, importFnNames: string[], importPath: string) => {
// ESM Imports
// import { importNames } from 'importPath';

const importSpecifiers = importFnNames.map((importKey) => {
const splt = importKey.split(' as ');
let importAs = importKey;
let importFnName = importKey;

if (splt.length > 1) {
importAs = splt[1];
importFnName = splt[0];
}

return ts.createImportSpecifier(
typeof importFnName === 'string' && importFnName !== importAs ? ts.createIdentifier(importFnName) : undefined,
ts.createIdentifier(importAs)
);
});

const newImport = createImportStatement(importFnNames, importPath);
const statements = tsSourceFile.statements.slice();
const newImport = ts.createImportDeclaration(
undefined,
undefined,
ts.createImportClause(undefined, ts.createNamedImports(importSpecifiers)),
ts.createLiteral(importPath)
);
statements.unshift(newImport);
return ts.updateSourceFileNode(tsSourceFile, statements);
};

const addCjsRequires = (tsSourceFile: ts.SourceFile, importFnNames: string[], importPath: string) => {
// CommonJS require()
// const { a, b, c } = require(importPath);

const importBinding = ts.createObjectBindingPattern(
importFnNames.map((importKey) => {
const splt = importKey.split(' as ');
let importAs = importKey;
let importFnName = importKey;

if (splt.length > 1) {
importAs = splt[1];
importFnName = splt[0];
}
return ts.createBindingElement(undefined, importFnName, importAs);
})
);

const requireStatement = ts.createVariableStatement(
undefined,
ts.createVariableDeclarationList(
[
ts.createVariableDeclaration(
importBinding,
undefined,
ts.createCall(ts.createIdentifier('require'), [], [ts.createLiteral(importPath)])
),
],
ts.NodeFlags.Const
)
);

const statements = tsSourceFile.statements.slice();

statements.splice(2, 0, requireStatement);

return ts.updateSourceFileNode(tsSourceFile, statements);
};
Loading

0 comments on commit 6987e43

Please sign in to comment.