@stencil/angular-output-target 1.3.1 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/generate-angular-component.js +4 -4
- package/dist/generate-value-accessors.js +8 -2
- package/dist/{index.cjs.js → index.cjs} +67 -38
- package/dist/index.js +66 -37
- package/dist/output-angular.d.ts +3 -2
- package/dist/output-angular.js +37 -25
- package/dist/plugin.js +7 -2
- package/dist/utils.js +9 -2
- package/dist/wizard.d.ts +9 -0
- package/dist/wizard.js +334 -0
- package/package.json +16 -4
|
@@ -90,7 +90,7 @@ export const createAngularComponentDefinition = (tagName, inputs, methods, inclu
|
|
|
90
90
|
.filter((event) => !event.internal)
|
|
91
91
|
.map((event) => {
|
|
92
92
|
const camelCaseOutput = event.name.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
93
|
-
const outputType = `EventEmitter
|
|
93
|
+
const outputType = `EventEmitter<${tagNameAsPascal}CustomEvent<${formatOutputType(tagNameAsPascal, event)}>>`;
|
|
94
94
|
return `@Output() ${camelCaseOutput} = new ${outputType}();`;
|
|
95
95
|
});
|
|
96
96
|
const propertiesDeclarationText = [
|
|
@@ -164,8 +164,8 @@ const formatOutputType = (componentClassName, event) => {
|
|
|
164
164
|
.replace(new RegExp(`([^\\w])${src}([^\\w])`, 'g'), (_, p1, p2) => {
|
|
165
165
|
/**
|
|
166
166
|
* Replaces a complex type reference within a generic type.
|
|
167
|
-
* For example, remapping a type like `EventEmitter<
|
|
168
|
-
* `EventEmitter<
|
|
167
|
+
* For example, remapping a type like `EventEmitter<MyComponentCustomEvent<MyEvent<T>>>` to
|
|
168
|
+
* `EventEmitter<MyComponentCustomEvent<IMyComponentMyEvent<IMyComponentT>>>`.
|
|
169
169
|
*/
|
|
170
170
|
return [p1, prefixedTypeName, p2].join('');
|
|
171
171
|
})
|
|
@@ -209,7 +209,7 @@ export const createComponentTypeDefinition = (outputType, tagNameAsPascal, event
|
|
|
209
209
|
customElementsDir,
|
|
210
210
|
outputType,
|
|
211
211
|
});
|
|
212
|
-
const eventTypes = publicEvents.map((event) => createPropertyDeclaration(event, `EventEmitter
|
|
212
|
+
const eventTypes = publicEvents.map((event) => createPropertyDeclaration(event, `EventEmitter<${tagNameAsPascal}CustomEvent<${formatOutputType(tagNameAsPascal, event)}>>`));
|
|
213
213
|
const interfaceDeclaration = `export declare interface ${tagNameAsPascal} extends Components.${tagNameAsPascal} {`;
|
|
214
214
|
const typeDefinition = (eventTypeImports.length > 0 ? `${eventTypeImports + '\n\n'}` : '') +
|
|
215
215
|
`${interfaceDeclaration}${eventTypes.length === 0
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { EOL } from 'os';
|
|
2
2
|
import path from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
5
|
+
const __dirname = path.dirname(__filename);
|
|
3
6
|
import { OutputTypes } from './utils';
|
|
4
7
|
export default async function generateValueAccessors(compilerCtx, components, outputTarget, config) {
|
|
5
8
|
if (!Array.isArray(outputTarget.valueAccessorConfigs) || outputTarget.valueAccessorConfigs.length === 0) {
|
|
@@ -15,10 +18,13 @@ export default async function generateValueAccessors(compilerCtx, components, ou
|
|
|
15
18
|
allElementSelectors = allAccessors[type].elementSelectors;
|
|
16
19
|
allEventTargets = allAccessors[type].eventTargets;
|
|
17
20
|
}
|
|
18
|
-
return
|
|
21
|
+
return {
|
|
22
|
+
...allAccessors,
|
|
23
|
+
[type]: {
|
|
19
24
|
elementSelectors: allElementSelectors.concat(elementSelectors),
|
|
20
25
|
eventTargets: allEventTargets.concat([[va.event, va.targetAttr]]),
|
|
21
|
-
}
|
|
26
|
+
},
|
|
27
|
+
};
|
|
22
28
|
}, {});
|
|
23
29
|
await Promise.all(Object.keys(normalizedValueAccessors).map(async (type) => {
|
|
24
30
|
const valueAccessorType = type; // Object.keys converts to string
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
5
|
var path = require('path');
|
|
6
|
+
var url = require('url');
|
|
6
7
|
var os = require('os');
|
|
7
8
|
|
|
8
9
|
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
@@ -69,11 +70,10 @@ function relativeImport(pathFrom, pathTo, ext) {
|
|
|
69
70
|
return normalizePath(`${relativePath}/${path__default["default"].basename(pathTo, ext)}`);
|
|
70
71
|
}
|
|
71
72
|
async function readPackageJson(config, rootDir) {
|
|
72
|
-
var _a;
|
|
73
73
|
const pkgJsonPath = path__default["default"].join(rootDir, 'package.json');
|
|
74
74
|
let pkgJson;
|
|
75
75
|
try {
|
|
76
|
-
pkgJson = (await
|
|
76
|
+
pkgJson = (await config.sys?.readFile(pkgJsonPath, 'utf8'));
|
|
77
77
|
}
|
|
78
78
|
catch (e) {
|
|
79
79
|
throw new Error(`Missing "package.json" file for distribution: ${pkgJsonPath}`);
|
|
@@ -127,6 +127,14 @@ const createComponentEventTypeImports = (componentTagName, events, options) => {
|
|
|
127
127
|
const namedImports = new Set();
|
|
128
128
|
const isCustomElementsBuild = isOutputTypeCustomElementsBuild(options.outputType);
|
|
129
129
|
const importPathName = normalizePath(componentCorePackage) + (isCustomElementsBuild ? `/${customElementsDir}` : '');
|
|
130
|
+
/**
|
|
131
|
+
* Each component's events are typed with the per-component CustomEvent type
|
|
132
|
+
* generated by Stencil (e.g. `MyComponentCustomEvent<Detail>`), which carries a
|
|
133
|
+
* narrowed `target`. Import it once per component that emits public events.
|
|
134
|
+
*/
|
|
135
|
+
if (events.length > 0) {
|
|
136
|
+
imports.push(`import type { ${componentTagName}CustomEvent } from '${importPathName}';`);
|
|
137
|
+
}
|
|
130
138
|
events.forEach((event) => {
|
|
131
139
|
Object.entries(event.complexType.references).forEach(([typeName, refObject]) => {
|
|
132
140
|
if (refObject.location === 'local' || refObject.location === 'import') {
|
|
@@ -236,7 +244,7 @@ const createAngularComponentDefinition = (tagName, inputs, methods, includeImpor
|
|
|
236
244
|
.filter((event) => !event.internal)
|
|
237
245
|
.map((event) => {
|
|
238
246
|
const camelCaseOutput = event.name.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
239
|
-
const outputType = `EventEmitter
|
|
247
|
+
const outputType = `EventEmitter<${tagNameAsPascal}CustomEvent<${formatOutputType(tagNameAsPascal, event)}>>`;
|
|
240
248
|
return `@Output() ${camelCaseOutput} = new ${outputType}();`;
|
|
241
249
|
});
|
|
242
250
|
const propertiesDeclarationText = [
|
|
@@ -310,8 +318,8 @@ const formatOutputType = (componentClassName, event) => {
|
|
|
310
318
|
.replace(new RegExp(`([^\\w])${src}([^\\w])`, 'g'), (_, p1, p2) => {
|
|
311
319
|
/**
|
|
312
320
|
* Replaces a complex type reference within a generic type.
|
|
313
|
-
* For example, remapping a type like `EventEmitter<
|
|
314
|
-
* `EventEmitter<
|
|
321
|
+
* For example, remapping a type like `EventEmitter<MyComponentCustomEvent<MyEvent<T>>>` to
|
|
322
|
+
* `EventEmitter<MyComponentCustomEvent<IMyComponentMyEvent<IMyComponentT>>>`.
|
|
315
323
|
*/
|
|
316
324
|
return [p1, prefixedTypeName, p2].join('');
|
|
317
325
|
})
|
|
@@ -355,7 +363,7 @@ const createComponentTypeDefinition = (outputType, tagNameAsPascal, events, comp
|
|
|
355
363
|
customElementsDir,
|
|
356
364
|
outputType,
|
|
357
365
|
});
|
|
358
|
-
const eventTypes = publicEvents.map((event) => createPropertyDeclaration(event, `EventEmitter
|
|
366
|
+
const eventTypes = publicEvents.map((event) => createPropertyDeclaration(event, `EventEmitter<${tagNameAsPascal}CustomEvent<${formatOutputType(tagNameAsPascal, event)}>>`));
|
|
359
367
|
const interfaceDeclaration = `export declare interface ${tagNameAsPascal} extends Components.${tagNameAsPascal} {`;
|
|
360
368
|
const typeDefinition = (eventTypeImports.length > 0 ? `${eventTypeImports + '\n\n'}` : '') +
|
|
361
369
|
`${interfaceDeclaration}${eventTypes.length === 0
|
|
@@ -386,6 +394,8 @@ export const DIRECTIVES = [
|
|
|
386
394
|
return compilerCtx.fs.writeFile(outputTarget.directivesArrayFile, c);
|
|
387
395
|
}
|
|
388
396
|
|
|
397
|
+
const __filename$2 = url.fileURLToPath((typeof document === 'undefined' ? new (require('u' + 'rl').URL)('file:' + __filename).href : (document.currentScript && document.currentScript.tagName.toUpperCase() === 'SCRIPT' && document.currentScript.src || new URL('index.cjs', document.baseURI).href)));
|
|
398
|
+
const __dirname$2 = path__default["default"].dirname(__filename$2);
|
|
389
399
|
async function generateValueAccessors(compilerCtx, components, outputTarget, config) {
|
|
390
400
|
if (!Array.isArray(outputTarget.valueAccessorConfigs) || outputTarget.valueAccessorConfigs.length === 0) {
|
|
391
401
|
return;
|
|
@@ -400,16 +410,19 @@ async function generateValueAccessors(compilerCtx, components, outputTarget, con
|
|
|
400
410
|
allElementSelectors = allAccessors[type].elementSelectors;
|
|
401
411
|
allEventTargets = allAccessors[type].eventTargets;
|
|
402
412
|
}
|
|
403
|
-
return
|
|
413
|
+
return {
|
|
414
|
+
...allAccessors,
|
|
415
|
+
[type]: {
|
|
404
416
|
elementSelectors: allElementSelectors.concat(elementSelectors),
|
|
405
417
|
eventTargets: allEventTargets.concat([[va.event, va.targetAttr]]),
|
|
406
|
-
}
|
|
418
|
+
},
|
|
419
|
+
};
|
|
407
420
|
}, {});
|
|
408
421
|
await Promise.all(Object.keys(normalizedValueAccessors).map(async (type) => {
|
|
409
422
|
const valueAccessorType = type; // Object.keys converts to string
|
|
410
423
|
const targetFileName = `${type}-value-accessor.ts`;
|
|
411
424
|
const targetFilePath = path__default["default"].join(targetDir, targetFileName);
|
|
412
|
-
const srcFilePath = path__default["default"].join(__dirname, '../resources/control-value-accessors/', targetFileName);
|
|
425
|
+
const srcFilePath = path__default["default"].join(__dirname$2, '../resources/control-value-accessors/', targetFileName);
|
|
413
426
|
const srcFileContents = await compilerCtx.fs.readFile(srcFilePath);
|
|
414
427
|
const finalText = createValueAccessor(srcFileContents, normalizedValueAccessors[valueAccessorType], outputTarget.outputType);
|
|
415
428
|
await compilerCtx.fs.writeFile(targetFilePath, finalText);
|
|
@@ -429,7 +442,7 @@ function copyResources$1(config, resourcesFilesToCopy, directory) {
|
|
|
429
442
|
}
|
|
430
443
|
const copyTasks = resourcesFilesToCopy.map((rf) => {
|
|
431
444
|
return {
|
|
432
|
-
src: path__default["default"].join(__dirname, '../resources/control-value-accessors/', rf),
|
|
445
|
+
src: path__default["default"].join(__dirname$2, '../resources/control-value-accessors/', rf),
|
|
433
446
|
dest: path__default["default"].join(directory, rf),
|
|
434
447
|
keepDirStructure: false,
|
|
435
448
|
warn: false,
|
|
@@ -767,6 +780,8 @@ stencilSetTagTransformer(\${transformerArg});
|
|
|
767
780
|
await compilerCtx.fs.writeFile(path__default["default"].join(scriptsDirectory, 'patch-transform-selectors.mjs'), patchSelectorsContent);
|
|
768
781
|
}
|
|
769
782
|
|
|
783
|
+
const __filename$1 = url.fileURLToPath((typeof document === 'undefined' ? new (require('u' + 'rl').URL)('file:' + __filename).href : (document.currentScript && document.currentScript.tagName.toUpperCase() === 'SCRIPT' && document.currentScript.src || new URL('index.cjs', document.baseURI).href)));
|
|
784
|
+
const __dirname$1 = path__default["default"].dirname(__filename$1);
|
|
770
785
|
async function angularDirectiveProxyOutput(compilerCtx, outputTarget, components, config) {
|
|
771
786
|
const filteredComponents = getFilteredComponents(outputTarget.excludeComponents, components);
|
|
772
787
|
const rootDir = config.rootDir;
|
|
@@ -783,7 +798,7 @@ async function angularDirectiveProxyOutput(compilerCtx, outputTarget, components
|
|
|
783
798
|
const proxiesDir = path__default["default"].dirname(outputTarget.directivesProxyFile);
|
|
784
799
|
for (const component of filteredComponents) {
|
|
785
800
|
const componentFile = path__default["default"].join(proxiesDir, `${component.tagName}.ts`);
|
|
786
|
-
const componentText = generateComponentProxy(component, pkgData, outputTarget, rootDir);
|
|
801
|
+
const componentText = generateComponentProxy(component, pkgData, outputTarget, rootDir, config);
|
|
787
802
|
tasks.push(compilerCtx.fs.writeFile(componentFile, componentText));
|
|
788
803
|
}
|
|
789
804
|
// Generate barrel file that re-exports all components
|
|
@@ -794,7 +809,7 @@ async function angularDirectiveProxyOutput(compilerCtx, outputTarget, components
|
|
|
794
809
|
}
|
|
795
810
|
else {
|
|
796
811
|
// Generate single file with all components (original behavior)
|
|
797
|
-
const finalText = generateProxies(filteredComponents, pkgData, outputTarget, rootDir);
|
|
812
|
+
const finalText = generateProxies(filteredComponents, pkgData, outputTarget, rootDir, config);
|
|
798
813
|
tasks.push(compilerCtx.fs.writeFile(outputTarget.directivesProxyFile, finalText));
|
|
799
814
|
tasks.push(generateAngularDirectivesFile(compilerCtx, filteredComponents, outputTarget));
|
|
800
815
|
}
|
|
@@ -831,7 +846,7 @@ async function copyResources(config, outputTarget) {
|
|
|
831
846
|
if (!config.sys || !config.sys.copy || !config.sys.glob) {
|
|
832
847
|
throw new Error('stencil is not properly initialized at this step. Notify the developer');
|
|
833
848
|
}
|
|
834
|
-
const srcDirectory = path__default["default"].join(__dirname, '..', 'angular-component-lib');
|
|
849
|
+
const srcDirectory = path__default["default"].join(__dirname$1, '..', 'angular-component-lib');
|
|
835
850
|
const destDirectory = path__default["default"].join(path__default["default"].dirname(outputTarget.directivesProxyFile), 'angular-component-lib');
|
|
836
851
|
return config.sys.copy([
|
|
837
852
|
{
|
|
@@ -843,7 +858,7 @@ async function copyResources(config, outputTarget) {
|
|
|
843
858
|
},
|
|
844
859
|
], srcDirectory);
|
|
845
860
|
}
|
|
846
|
-
function generateProxies(components, pkgData, outputTarget, rootDir) {
|
|
861
|
+
function generateProxies(components, pkgData, outputTarget, rootDir, config) {
|
|
847
862
|
const distTypesDir = path__default["default"].dirname(pkgData.types);
|
|
848
863
|
const dtsFilePath = path__default["default"].join(rootDir, distTypesDir, GENERATED_DTS);
|
|
849
864
|
const { outputType } = outputTarget;
|
|
@@ -879,10 +894,9 @@ ${createImportStatement(componentLibImports, './angular-component-lib/utils')}\n
|
|
|
879
894
|
* otherwise we risk bundlers pulling in lazy loaded imports.
|
|
880
895
|
*/
|
|
881
896
|
const generateTypeImports = () => {
|
|
882
|
-
|
|
883
|
-
?
|
|
897
|
+
const importLocation = outputTarget.componentCorePackage
|
|
898
|
+
? getPathToComponentTypes(config, outputTarget)
|
|
884
899
|
: normalizePath(componentsTypeFile);
|
|
885
|
-
importLocation += isCustomElementsBuild ? `/${outputTarget.customElementsDir}` : '';
|
|
886
900
|
return `import ${isCustomElementsBuild ? 'type ' : ''}{ ${IMPORT_TYPES} } from '${importLocation}';\n`;
|
|
887
901
|
};
|
|
888
902
|
const typeImports = generateTypeImports();
|
|
@@ -903,13 +917,10 @@ ${createImportStatement(componentLibImports, './angular-component-lib/utils')}\n
|
|
|
903
917
|
const proxyFileOutput = [];
|
|
904
918
|
const filterInternalProps = (prop) => !prop.internal;
|
|
905
919
|
// Ensure that virtual properties has required as false.
|
|
906
|
-
const mapInputProp = (prop) => {
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
required: (_a = prop.required) !== null && _a !== void 0 ? _a : false,
|
|
911
|
-
});
|
|
912
|
-
};
|
|
920
|
+
const mapInputProp = (prop) => ({
|
|
921
|
+
name: prop.name,
|
|
922
|
+
required: prop.required ?? false,
|
|
923
|
+
});
|
|
913
924
|
const { componentCorePackage, customElementsDir } = outputTarget;
|
|
914
925
|
for (let cmpMeta of components) {
|
|
915
926
|
const tagNameAsPascal = dashToPascalCase(cmpMeta.tagName);
|
|
@@ -948,8 +959,7 @@ ${createImportStatement(componentLibImports, './angular-component-lib/utils')}\n
|
|
|
948
959
|
/**
|
|
949
960
|
* Generate a single component proxy file for ES modules output
|
|
950
961
|
*/
|
|
951
|
-
function generateComponentProxy(cmpMeta, pkgData, outputTarget, rootDir) {
|
|
952
|
-
var _a;
|
|
962
|
+
function generateComponentProxy(cmpMeta, pkgData, outputTarget, rootDir, config) {
|
|
953
963
|
const { outputType, componentCorePackage, customElementsDir } = outputTarget;
|
|
954
964
|
const distTypesDir = path__default["default"].dirname(pkgData.types);
|
|
955
965
|
const dtsFilePath = path__default["default"].join(rootDir, distTypesDir, GENERATED_DTS);
|
|
@@ -958,7 +968,7 @@ function generateComponentProxy(cmpMeta, pkgData, outputTarget, rootDir) {
|
|
|
958
968
|
const isCustomElementsBuild = isOutputTypeCustomElementsBuild(outputType);
|
|
959
969
|
const isStandaloneBuild = outputType === OutputTypes.Standalone;
|
|
960
970
|
const tagNameAsPascal = dashToPascalCase(cmpMeta.tagName);
|
|
961
|
-
const hasOutputs =
|
|
971
|
+
const hasOutputs = cmpMeta.events?.some((event) => !event.internal);
|
|
962
972
|
// Angular core imports for this component
|
|
963
973
|
const angularCoreImports = ['ChangeDetectionStrategy', 'ChangeDetectorRef', 'Component', 'ElementRef', 'NgZone'];
|
|
964
974
|
if (hasOutputs) {
|
|
@@ -973,8 +983,9 @@ ${createImportStatement(angularCoreImports, '@angular/core')}
|
|
|
973
983
|
|
|
974
984
|
${createImportStatement(['ProxyCmp'], './angular-component-lib/utils')}\n`;
|
|
975
985
|
// Type imports
|
|
976
|
-
|
|
977
|
-
|
|
986
|
+
const importLocation = componentCorePackage
|
|
987
|
+
? getPathToComponentTypes(config, outputTarget)
|
|
988
|
+
: normalizePath(componentsTypeFile);
|
|
978
989
|
const typeImports = `import ${isCustomElementsBuild ? 'type ' : ''}{ ${IMPORT_TYPES} } from '${importLocation}';\n`;
|
|
979
990
|
// defineCustomElement import
|
|
980
991
|
let sourceImport = '';
|
|
@@ -983,13 +994,10 @@ ${createImportStatement(['ProxyCmp'], './angular-component-lib/utils')}\n`;
|
|
|
983
994
|
}
|
|
984
995
|
// Generate component definition
|
|
985
996
|
const filterInternalProps = (prop) => !prop.internal;
|
|
986
|
-
const mapInputProp = (prop) => {
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
required: (_a = prop.required) !== null && _a !== void 0 ? _a : false,
|
|
991
|
-
});
|
|
992
|
-
};
|
|
997
|
+
const mapInputProp = (prop) => ({
|
|
998
|
+
name: prop.name,
|
|
999
|
+
required: prop.required ?? false,
|
|
1000
|
+
});
|
|
993
1001
|
const internalProps = [];
|
|
994
1002
|
if (cmpMeta.properties) {
|
|
995
1003
|
internalProps.push(...cmpMeta.properties.filter(filterInternalProps));
|
|
@@ -1035,6 +1043,22 @@ function generateBarrelFile(components, outputTarget) {
|
|
|
1035
1043
|
.join('\n');
|
|
1036
1044
|
return header + exports + '\n';
|
|
1037
1045
|
}
|
|
1046
|
+
function getPathToComponentTypes(config, outputTarget) {
|
|
1047
|
+
const basePkg = outputTarget.componentCorePackage || '';
|
|
1048
|
+
// in v5, all types (including components.d.ts) are generated in the dist/types directory
|
|
1049
|
+
const typesTarget = config.outputTargets?.find((o) => o.type === 'types');
|
|
1050
|
+
if (typesTarget) {
|
|
1051
|
+
const rawDir = typesTarget.dir || 'dist/types';
|
|
1052
|
+
const relDir = config.rootDir && path__default["default"].isAbsolute(rawDir) ? path__default["default"].relative(config.rootDir, rawDir) : rawDir;
|
|
1053
|
+
return normalizePath(path__default["default"].join(basePkg, relDir, 'components'));
|
|
1054
|
+
}
|
|
1055
|
+
// v4: append customElementsDir for custom elements build, otherwise package root
|
|
1056
|
+
const isCustomElementsBuild = isOutputTypeCustomElementsBuild(outputTarget.outputType);
|
|
1057
|
+
if (isCustomElementsBuild && outputTarget.customElementsDir) {
|
|
1058
|
+
return normalizePath(path__default["default"].join(basePkg, outputTarget.customElementsDir));
|
|
1059
|
+
}
|
|
1060
|
+
return normalizePath(basePkg);
|
|
1061
|
+
}
|
|
1038
1062
|
const GENERATED_DTS = 'components.d.ts';
|
|
1039
1063
|
const IMPORT_TYPES = 'Components';
|
|
1040
1064
|
|
|
@@ -1054,8 +1078,13 @@ const angularOutputTarget = (outputTarget) => {
|
|
|
1054
1078
|
};
|
|
1055
1079
|
};
|
|
1056
1080
|
function normalizeOutputTarget(config, outputTarget) {
|
|
1057
|
-
|
|
1058
|
-
|
|
1081
|
+
const results = {
|
|
1082
|
+
...outputTarget,
|
|
1083
|
+
excludeComponents: outputTarget.excludeComponents || [],
|
|
1084
|
+
valueAccessorConfigs: outputTarget.valueAccessorConfigs || [],
|
|
1085
|
+
customElementsDir: outputTarget.customElementsDir ?? 'components',
|
|
1086
|
+
outputType: outputTarget.outputType ?? OutputTypes.Standalone,
|
|
1087
|
+
};
|
|
1059
1088
|
if (config.rootDir == null) {
|
|
1060
1089
|
throw new Error('rootDir is not set and it should be set by stencil itself');
|
|
1061
1090
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
|
+
import { fileURLToPath } from 'url';
|
|
2
3
|
import { EOL } from 'os';
|
|
3
4
|
|
|
4
5
|
const OutputTypes = {
|
|
@@ -61,11 +62,10 @@ function relativeImport(pathFrom, pathTo, ext) {
|
|
|
61
62
|
return normalizePath(`${relativePath}/${path.basename(pathTo, ext)}`);
|
|
62
63
|
}
|
|
63
64
|
async function readPackageJson(config, rootDir) {
|
|
64
|
-
var _a;
|
|
65
65
|
const pkgJsonPath = path.join(rootDir, 'package.json');
|
|
66
66
|
let pkgJson;
|
|
67
67
|
try {
|
|
68
|
-
pkgJson = (await
|
|
68
|
+
pkgJson = (await config.sys?.readFile(pkgJsonPath, 'utf8'));
|
|
69
69
|
}
|
|
70
70
|
catch (e) {
|
|
71
71
|
throw new Error(`Missing "package.json" file for distribution: ${pkgJsonPath}`);
|
|
@@ -119,6 +119,14 @@ const createComponentEventTypeImports = (componentTagName, events, options) => {
|
|
|
119
119
|
const namedImports = new Set();
|
|
120
120
|
const isCustomElementsBuild = isOutputTypeCustomElementsBuild(options.outputType);
|
|
121
121
|
const importPathName = normalizePath(componentCorePackage) + (isCustomElementsBuild ? `/${customElementsDir}` : '');
|
|
122
|
+
/**
|
|
123
|
+
* Each component's events are typed with the per-component CustomEvent type
|
|
124
|
+
* generated by Stencil (e.g. `MyComponentCustomEvent<Detail>`), which carries a
|
|
125
|
+
* narrowed `target`. Import it once per component that emits public events.
|
|
126
|
+
*/
|
|
127
|
+
if (events.length > 0) {
|
|
128
|
+
imports.push(`import type { ${componentTagName}CustomEvent } from '${importPathName}';`);
|
|
129
|
+
}
|
|
122
130
|
events.forEach((event) => {
|
|
123
131
|
Object.entries(event.complexType.references).forEach(([typeName, refObject]) => {
|
|
124
132
|
if (refObject.location === 'local' || refObject.location === 'import') {
|
|
@@ -228,7 +236,7 @@ const createAngularComponentDefinition = (tagName, inputs, methods, includeImpor
|
|
|
228
236
|
.filter((event) => !event.internal)
|
|
229
237
|
.map((event) => {
|
|
230
238
|
const camelCaseOutput = event.name.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
231
|
-
const outputType = `EventEmitter
|
|
239
|
+
const outputType = `EventEmitter<${tagNameAsPascal}CustomEvent<${formatOutputType(tagNameAsPascal, event)}>>`;
|
|
232
240
|
return `@Output() ${camelCaseOutput} = new ${outputType}();`;
|
|
233
241
|
});
|
|
234
242
|
const propertiesDeclarationText = [
|
|
@@ -302,8 +310,8 @@ const formatOutputType = (componentClassName, event) => {
|
|
|
302
310
|
.replace(new RegExp(`([^\\w])${src}([^\\w])`, 'g'), (_, p1, p2) => {
|
|
303
311
|
/**
|
|
304
312
|
* Replaces a complex type reference within a generic type.
|
|
305
|
-
* For example, remapping a type like `EventEmitter<
|
|
306
|
-
* `EventEmitter<
|
|
313
|
+
* For example, remapping a type like `EventEmitter<MyComponentCustomEvent<MyEvent<T>>>` to
|
|
314
|
+
* `EventEmitter<MyComponentCustomEvent<IMyComponentMyEvent<IMyComponentT>>>`.
|
|
307
315
|
*/
|
|
308
316
|
return [p1, prefixedTypeName, p2].join('');
|
|
309
317
|
})
|
|
@@ -347,7 +355,7 @@ const createComponentTypeDefinition = (outputType, tagNameAsPascal, events, comp
|
|
|
347
355
|
customElementsDir,
|
|
348
356
|
outputType,
|
|
349
357
|
});
|
|
350
|
-
const eventTypes = publicEvents.map((event) => createPropertyDeclaration(event, `EventEmitter
|
|
358
|
+
const eventTypes = publicEvents.map((event) => createPropertyDeclaration(event, `EventEmitter<${tagNameAsPascal}CustomEvent<${formatOutputType(tagNameAsPascal, event)}>>`));
|
|
351
359
|
const interfaceDeclaration = `export declare interface ${tagNameAsPascal} extends Components.${tagNameAsPascal} {`;
|
|
352
360
|
const typeDefinition = (eventTypeImports.length > 0 ? `${eventTypeImports + '\n\n'}` : '') +
|
|
353
361
|
`${interfaceDeclaration}${eventTypes.length === 0
|
|
@@ -378,6 +386,8 @@ export const DIRECTIVES = [
|
|
|
378
386
|
return compilerCtx.fs.writeFile(outputTarget.directivesArrayFile, c);
|
|
379
387
|
}
|
|
380
388
|
|
|
389
|
+
const __filename$1 = fileURLToPath(import.meta.url);
|
|
390
|
+
const __dirname$1 = path.dirname(__filename$1);
|
|
381
391
|
async function generateValueAccessors(compilerCtx, components, outputTarget, config) {
|
|
382
392
|
if (!Array.isArray(outputTarget.valueAccessorConfigs) || outputTarget.valueAccessorConfigs.length === 0) {
|
|
383
393
|
return;
|
|
@@ -392,16 +402,19 @@ async function generateValueAccessors(compilerCtx, components, outputTarget, con
|
|
|
392
402
|
allElementSelectors = allAccessors[type].elementSelectors;
|
|
393
403
|
allEventTargets = allAccessors[type].eventTargets;
|
|
394
404
|
}
|
|
395
|
-
return
|
|
405
|
+
return {
|
|
406
|
+
...allAccessors,
|
|
407
|
+
[type]: {
|
|
396
408
|
elementSelectors: allElementSelectors.concat(elementSelectors),
|
|
397
409
|
eventTargets: allEventTargets.concat([[va.event, va.targetAttr]]),
|
|
398
|
-
}
|
|
410
|
+
},
|
|
411
|
+
};
|
|
399
412
|
}, {});
|
|
400
413
|
await Promise.all(Object.keys(normalizedValueAccessors).map(async (type) => {
|
|
401
414
|
const valueAccessorType = type; // Object.keys converts to string
|
|
402
415
|
const targetFileName = `${type}-value-accessor.ts`;
|
|
403
416
|
const targetFilePath = path.join(targetDir, targetFileName);
|
|
404
|
-
const srcFilePath = path.join(__dirname, '../resources/control-value-accessors/', targetFileName);
|
|
417
|
+
const srcFilePath = path.join(__dirname$1, '../resources/control-value-accessors/', targetFileName);
|
|
405
418
|
const srcFileContents = await compilerCtx.fs.readFile(srcFilePath);
|
|
406
419
|
const finalText = createValueAccessor(srcFileContents, normalizedValueAccessors[valueAccessorType], outputTarget.outputType);
|
|
407
420
|
await compilerCtx.fs.writeFile(targetFilePath, finalText);
|
|
@@ -421,7 +434,7 @@ function copyResources$1(config, resourcesFilesToCopy, directory) {
|
|
|
421
434
|
}
|
|
422
435
|
const copyTasks = resourcesFilesToCopy.map((rf) => {
|
|
423
436
|
return {
|
|
424
|
-
src: path.join(__dirname, '../resources/control-value-accessors/', rf),
|
|
437
|
+
src: path.join(__dirname$1, '../resources/control-value-accessors/', rf),
|
|
425
438
|
dest: path.join(directory, rf),
|
|
426
439
|
keepDirStructure: false,
|
|
427
440
|
warn: false,
|
|
@@ -759,6 +772,8 @@ stencilSetTagTransformer(\${transformerArg});
|
|
|
759
772
|
await compilerCtx.fs.writeFile(path.join(scriptsDirectory, 'patch-transform-selectors.mjs'), patchSelectorsContent);
|
|
760
773
|
}
|
|
761
774
|
|
|
775
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
776
|
+
const __dirname = path.dirname(__filename);
|
|
762
777
|
async function angularDirectiveProxyOutput(compilerCtx, outputTarget, components, config) {
|
|
763
778
|
const filteredComponents = getFilteredComponents(outputTarget.excludeComponents, components);
|
|
764
779
|
const rootDir = config.rootDir;
|
|
@@ -775,7 +790,7 @@ async function angularDirectiveProxyOutput(compilerCtx, outputTarget, components
|
|
|
775
790
|
const proxiesDir = path.dirname(outputTarget.directivesProxyFile);
|
|
776
791
|
for (const component of filteredComponents) {
|
|
777
792
|
const componentFile = path.join(proxiesDir, `${component.tagName}.ts`);
|
|
778
|
-
const componentText = generateComponentProxy(component, pkgData, outputTarget, rootDir);
|
|
793
|
+
const componentText = generateComponentProxy(component, pkgData, outputTarget, rootDir, config);
|
|
779
794
|
tasks.push(compilerCtx.fs.writeFile(componentFile, componentText));
|
|
780
795
|
}
|
|
781
796
|
// Generate barrel file that re-exports all components
|
|
@@ -786,7 +801,7 @@ async function angularDirectiveProxyOutput(compilerCtx, outputTarget, components
|
|
|
786
801
|
}
|
|
787
802
|
else {
|
|
788
803
|
// Generate single file with all components (original behavior)
|
|
789
|
-
const finalText = generateProxies(filteredComponents, pkgData, outputTarget, rootDir);
|
|
804
|
+
const finalText = generateProxies(filteredComponents, pkgData, outputTarget, rootDir, config);
|
|
790
805
|
tasks.push(compilerCtx.fs.writeFile(outputTarget.directivesProxyFile, finalText));
|
|
791
806
|
tasks.push(generateAngularDirectivesFile(compilerCtx, filteredComponents, outputTarget));
|
|
792
807
|
}
|
|
@@ -835,7 +850,7 @@ async function copyResources(config, outputTarget) {
|
|
|
835
850
|
},
|
|
836
851
|
], srcDirectory);
|
|
837
852
|
}
|
|
838
|
-
function generateProxies(components, pkgData, outputTarget, rootDir) {
|
|
853
|
+
function generateProxies(components, pkgData, outputTarget, rootDir, config) {
|
|
839
854
|
const distTypesDir = path.dirname(pkgData.types);
|
|
840
855
|
const dtsFilePath = path.join(rootDir, distTypesDir, GENERATED_DTS);
|
|
841
856
|
const { outputType } = outputTarget;
|
|
@@ -871,10 +886,9 @@ ${createImportStatement(componentLibImports, './angular-component-lib/utils')}\n
|
|
|
871
886
|
* otherwise we risk bundlers pulling in lazy loaded imports.
|
|
872
887
|
*/
|
|
873
888
|
const generateTypeImports = () => {
|
|
874
|
-
|
|
875
|
-
?
|
|
889
|
+
const importLocation = outputTarget.componentCorePackage
|
|
890
|
+
? getPathToComponentTypes(config, outputTarget)
|
|
876
891
|
: normalizePath(componentsTypeFile);
|
|
877
|
-
importLocation += isCustomElementsBuild ? `/${outputTarget.customElementsDir}` : '';
|
|
878
892
|
return `import ${isCustomElementsBuild ? 'type ' : ''}{ ${IMPORT_TYPES} } from '${importLocation}';\n`;
|
|
879
893
|
};
|
|
880
894
|
const typeImports = generateTypeImports();
|
|
@@ -895,13 +909,10 @@ ${createImportStatement(componentLibImports, './angular-component-lib/utils')}\n
|
|
|
895
909
|
const proxyFileOutput = [];
|
|
896
910
|
const filterInternalProps = (prop) => !prop.internal;
|
|
897
911
|
// Ensure that virtual properties has required as false.
|
|
898
|
-
const mapInputProp = (prop) => {
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
required: (_a = prop.required) !== null && _a !== void 0 ? _a : false,
|
|
903
|
-
});
|
|
904
|
-
};
|
|
912
|
+
const mapInputProp = (prop) => ({
|
|
913
|
+
name: prop.name,
|
|
914
|
+
required: prop.required ?? false,
|
|
915
|
+
});
|
|
905
916
|
const { componentCorePackage, customElementsDir } = outputTarget;
|
|
906
917
|
for (let cmpMeta of components) {
|
|
907
918
|
const tagNameAsPascal = dashToPascalCase(cmpMeta.tagName);
|
|
@@ -940,8 +951,7 @@ ${createImportStatement(componentLibImports, './angular-component-lib/utils')}\n
|
|
|
940
951
|
/**
|
|
941
952
|
* Generate a single component proxy file for ES modules output
|
|
942
953
|
*/
|
|
943
|
-
function generateComponentProxy(cmpMeta, pkgData, outputTarget, rootDir) {
|
|
944
|
-
var _a;
|
|
954
|
+
function generateComponentProxy(cmpMeta, pkgData, outputTarget, rootDir, config) {
|
|
945
955
|
const { outputType, componentCorePackage, customElementsDir } = outputTarget;
|
|
946
956
|
const distTypesDir = path.dirname(pkgData.types);
|
|
947
957
|
const dtsFilePath = path.join(rootDir, distTypesDir, GENERATED_DTS);
|
|
@@ -950,7 +960,7 @@ function generateComponentProxy(cmpMeta, pkgData, outputTarget, rootDir) {
|
|
|
950
960
|
const isCustomElementsBuild = isOutputTypeCustomElementsBuild(outputType);
|
|
951
961
|
const isStandaloneBuild = outputType === OutputTypes.Standalone;
|
|
952
962
|
const tagNameAsPascal = dashToPascalCase(cmpMeta.tagName);
|
|
953
|
-
const hasOutputs =
|
|
963
|
+
const hasOutputs = cmpMeta.events?.some((event) => !event.internal);
|
|
954
964
|
// Angular core imports for this component
|
|
955
965
|
const angularCoreImports = ['ChangeDetectionStrategy', 'ChangeDetectorRef', 'Component', 'ElementRef', 'NgZone'];
|
|
956
966
|
if (hasOutputs) {
|
|
@@ -965,8 +975,9 @@ ${createImportStatement(angularCoreImports, '@angular/core')}
|
|
|
965
975
|
|
|
966
976
|
${createImportStatement(['ProxyCmp'], './angular-component-lib/utils')}\n`;
|
|
967
977
|
// Type imports
|
|
968
|
-
|
|
969
|
-
|
|
978
|
+
const importLocation = componentCorePackage
|
|
979
|
+
? getPathToComponentTypes(config, outputTarget)
|
|
980
|
+
: normalizePath(componentsTypeFile);
|
|
970
981
|
const typeImports = `import ${isCustomElementsBuild ? 'type ' : ''}{ ${IMPORT_TYPES} } from '${importLocation}';\n`;
|
|
971
982
|
// defineCustomElement import
|
|
972
983
|
let sourceImport = '';
|
|
@@ -975,13 +986,10 @@ ${createImportStatement(['ProxyCmp'], './angular-component-lib/utils')}\n`;
|
|
|
975
986
|
}
|
|
976
987
|
// Generate component definition
|
|
977
988
|
const filterInternalProps = (prop) => !prop.internal;
|
|
978
|
-
const mapInputProp = (prop) => {
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
required: (_a = prop.required) !== null && _a !== void 0 ? _a : false,
|
|
983
|
-
});
|
|
984
|
-
};
|
|
989
|
+
const mapInputProp = (prop) => ({
|
|
990
|
+
name: prop.name,
|
|
991
|
+
required: prop.required ?? false,
|
|
992
|
+
});
|
|
985
993
|
const internalProps = [];
|
|
986
994
|
if (cmpMeta.properties) {
|
|
987
995
|
internalProps.push(...cmpMeta.properties.filter(filterInternalProps));
|
|
@@ -1027,6 +1035,22 @@ function generateBarrelFile(components, outputTarget) {
|
|
|
1027
1035
|
.join('\n');
|
|
1028
1036
|
return header + exports + '\n';
|
|
1029
1037
|
}
|
|
1038
|
+
function getPathToComponentTypes(config, outputTarget) {
|
|
1039
|
+
const basePkg = outputTarget.componentCorePackage || '';
|
|
1040
|
+
// in v5, all types (including components.d.ts) are generated in the dist/types directory
|
|
1041
|
+
const typesTarget = config.outputTargets?.find((o) => o.type === 'types');
|
|
1042
|
+
if (typesTarget) {
|
|
1043
|
+
const rawDir = typesTarget.dir || 'dist/types';
|
|
1044
|
+
const relDir = config.rootDir && path.isAbsolute(rawDir) ? path.relative(config.rootDir, rawDir) : rawDir;
|
|
1045
|
+
return normalizePath(path.join(basePkg, relDir, 'components'));
|
|
1046
|
+
}
|
|
1047
|
+
// v4: append customElementsDir for custom elements build, otherwise package root
|
|
1048
|
+
const isCustomElementsBuild = isOutputTypeCustomElementsBuild(outputTarget.outputType);
|
|
1049
|
+
if (isCustomElementsBuild && outputTarget.customElementsDir) {
|
|
1050
|
+
return normalizePath(path.join(basePkg, outputTarget.customElementsDir));
|
|
1051
|
+
}
|
|
1052
|
+
return normalizePath(basePkg);
|
|
1053
|
+
}
|
|
1030
1054
|
const GENERATED_DTS = 'components.d.ts';
|
|
1031
1055
|
const IMPORT_TYPES = 'Components';
|
|
1032
1056
|
|
|
@@ -1046,8 +1070,13 @@ const angularOutputTarget = (outputTarget) => {
|
|
|
1046
1070
|
};
|
|
1047
1071
|
};
|
|
1048
1072
|
function normalizeOutputTarget(config, outputTarget) {
|
|
1049
|
-
|
|
1050
|
-
|
|
1073
|
+
const results = {
|
|
1074
|
+
...outputTarget,
|
|
1075
|
+
excludeComponents: outputTarget.excludeComponents || [],
|
|
1076
|
+
valueAccessorConfigs: outputTarget.valueAccessorConfigs || [],
|
|
1077
|
+
customElementsDir: outputTarget.customElementsDir ?? 'components',
|
|
1078
|
+
outputType: outputTarget.outputType ?? OutputTypes.Standalone,
|
|
1079
|
+
};
|
|
1051
1080
|
if (config.rootDir == null) {
|
|
1052
1081
|
throw new Error('rootDir is not set and it should be set by stencil itself');
|
|
1053
1082
|
}
|
package/dist/output-angular.d.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import type { CompilerCtx, ComponentCompilerMeta, Config } from '@stencil/core/internal';
|
|
2
2
|
import type { OutputTargetAngular, PackageJSON } from './types';
|
|
3
3
|
export declare function angularDirectiveProxyOutput(compilerCtx: CompilerCtx, outputTarget: OutputTargetAngular, components: ComponentCompilerMeta[], config: Config): Promise<void>;
|
|
4
|
-
export declare function generateProxies(components: ComponentCompilerMeta[], pkgData: PackageJSON, outputTarget: OutputTargetAngular, rootDir: string): string;
|
|
4
|
+
export declare function generateProxies(components: ComponentCompilerMeta[], pkgData: PackageJSON, outputTarget: OutputTargetAngular, rootDir: string, config: Config): string;
|
|
5
5
|
/**
|
|
6
6
|
* Generate a single component proxy file for ES modules output
|
|
7
7
|
*/
|
|
8
|
-
export declare function generateComponentProxy(cmpMeta: ComponentCompilerMeta, pkgData: PackageJSON, outputTarget: OutputTargetAngular, rootDir: string): string;
|
|
8
|
+
export declare function generateComponentProxy(cmpMeta: ComponentCompilerMeta, pkgData: PackageJSON, outputTarget: OutputTargetAngular, rootDir: string, config: Config): string;
|
|
9
9
|
/**
|
|
10
10
|
* Generate a barrel file that re-exports all components
|
|
11
11
|
*/
|
|
12
12
|
export declare function generateBarrelFile(components: ComponentCompilerMeta[], outputTarget: OutputTargetAngular): string;
|
|
13
|
+
export declare function getPathToComponentTypes(config: Config, outputTarget: OutputTargetAngular): string;
|
package/dist/output-angular.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
|
+
import { fileURLToPath } from 'url';
|
|
3
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
4
|
+
const __dirname = path.dirname(__filename);
|
|
2
5
|
import { relativeImport, normalizePath, sortBy, readPackageJson, dashToPascalCase, createImportStatement, isOutputTypeCustomElementsBuild, OutputTypes, mapPropName, } from './utils';
|
|
3
6
|
import { createAngularComponentDefinition, createComponentTypeDefinition } from './generate-angular-component';
|
|
4
7
|
import { generateAngularDirectivesFile } from './generate-angular-directives-file';
|
|
@@ -21,7 +24,7 @@ export async function angularDirectiveProxyOutput(compilerCtx, outputTarget, com
|
|
|
21
24
|
const proxiesDir = path.dirname(outputTarget.directivesProxyFile);
|
|
22
25
|
for (const component of filteredComponents) {
|
|
23
26
|
const componentFile = path.join(proxiesDir, `${component.tagName}.ts`);
|
|
24
|
-
const componentText = generateComponentProxy(component, pkgData, outputTarget, rootDir);
|
|
27
|
+
const componentText = generateComponentProxy(component, pkgData, outputTarget, rootDir, config);
|
|
25
28
|
tasks.push(compilerCtx.fs.writeFile(componentFile, componentText));
|
|
26
29
|
}
|
|
27
30
|
// Generate barrel file that re-exports all components
|
|
@@ -32,7 +35,7 @@ export async function angularDirectiveProxyOutput(compilerCtx, outputTarget, com
|
|
|
32
35
|
}
|
|
33
36
|
else {
|
|
34
37
|
// Generate single file with all components (original behavior)
|
|
35
|
-
const finalText = generateProxies(filteredComponents, pkgData, outputTarget, rootDir);
|
|
38
|
+
const finalText = generateProxies(filteredComponents, pkgData, outputTarget, rootDir, config);
|
|
36
39
|
tasks.push(compilerCtx.fs.writeFile(outputTarget.directivesProxyFile, finalText));
|
|
37
40
|
tasks.push(generateAngularDirectivesFile(compilerCtx, filteredComponents, outputTarget));
|
|
38
41
|
}
|
|
@@ -81,7 +84,7 @@ async function copyResources(config, outputTarget) {
|
|
|
81
84
|
},
|
|
82
85
|
], srcDirectory);
|
|
83
86
|
}
|
|
84
|
-
export function generateProxies(components, pkgData, outputTarget, rootDir) {
|
|
87
|
+
export function generateProxies(components, pkgData, outputTarget, rootDir, config) {
|
|
85
88
|
const distTypesDir = path.dirname(pkgData.types);
|
|
86
89
|
const dtsFilePath = path.join(rootDir, distTypesDir, GENERATED_DTS);
|
|
87
90
|
const { outputType } = outputTarget;
|
|
@@ -117,10 +120,9 @@ ${createImportStatement(componentLibImports, './angular-component-lib/utils')}\n
|
|
|
117
120
|
* otherwise we risk bundlers pulling in lazy loaded imports.
|
|
118
121
|
*/
|
|
119
122
|
const generateTypeImports = () => {
|
|
120
|
-
|
|
121
|
-
?
|
|
123
|
+
const importLocation = outputTarget.componentCorePackage
|
|
124
|
+
? getPathToComponentTypes(config, outputTarget)
|
|
122
125
|
: normalizePath(componentsTypeFile);
|
|
123
|
-
importLocation += isCustomElementsBuild ? `/${outputTarget.customElementsDir}` : '';
|
|
124
126
|
return `import ${isCustomElementsBuild ? 'type ' : ''}{ ${IMPORT_TYPES} } from '${importLocation}';\n`;
|
|
125
127
|
};
|
|
126
128
|
const typeImports = generateTypeImports();
|
|
@@ -141,13 +143,10 @@ ${createImportStatement(componentLibImports, './angular-component-lib/utils')}\n
|
|
|
141
143
|
const proxyFileOutput = [];
|
|
142
144
|
const filterInternalProps = (prop) => !prop.internal;
|
|
143
145
|
// Ensure that virtual properties has required as false.
|
|
144
|
-
const mapInputProp = (prop) => {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
required: (_a = prop.required) !== null && _a !== void 0 ? _a : false,
|
|
149
|
-
});
|
|
150
|
-
};
|
|
146
|
+
const mapInputProp = (prop) => ({
|
|
147
|
+
name: prop.name,
|
|
148
|
+
required: prop.required ?? false,
|
|
149
|
+
});
|
|
151
150
|
const { componentCorePackage, customElementsDir } = outputTarget;
|
|
152
151
|
for (let cmpMeta of components) {
|
|
153
152
|
const tagNameAsPascal = dashToPascalCase(cmpMeta.tagName);
|
|
@@ -186,8 +185,7 @@ ${createImportStatement(componentLibImports, './angular-component-lib/utils')}\n
|
|
|
186
185
|
/**
|
|
187
186
|
* Generate a single component proxy file for ES modules output
|
|
188
187
|
*/
|
|
189
|
-
export function generateComponentProxy(cmpMeta, pkgData, outputTarget, rootDir) {
|
|
190
|
-
var _a;
|
|
188
|
+
export function generateComponentProxy(cmpMeta, pkgData, outputTarget, rootDir, config) {
|
|
191
189
|
const { outputType, componentCorePackage, customElementsDir } = outputTarget;
|
|
192
190
|
const distTypesDir = path.dirname(pkgData.types);
|
|
193
191
|
const dtsFilePath = path.join(rootDir, distTypesDir, GENERATED_DTS);
|
|
@@ -196,7 +194,7 @@ export function generateComponentProxy(cmpMeta, pkgData, outputTarget, rootDir)
|
|
|
196
194
|
const isCustomElementsBuild = isOutputTypeCustomElementsBuild(outputType);
|
|
197
195
|
const isStandaloneBuild = outputType === OutputTypes.Standalone;
|
|
198
196
|
const tagNameAsPascal = dashToPascalCase(cmpMeta.tagName);
|
|
199
|
-
const hasOutputs =
|
|
197
|
+
const hasOutputs = cmpMeta.events?.some((event) => !event.internal);
|
|
200
198
|
// Angular core imports for this component
|
|
201
199
|
const angularCoreImports = ['ChangeDetectionStrategy', 'ChangeDetectorRef', 'Component', 'ElementRef', 'NgZone'];
|
|
202
200
|
if (hasOutputs) {
|
|
@@ -211,8 +209,9 @@ ${createImportStatement(angularCoreImports, '@angular/core')}
|
|
|
211
209
|
|
|
212
210
|
${createImportStatement(['ProxyCmp'], './angular-component-lib/utils')}\n`;
|
|
213
211
|
// Type imports
|
|
214
|
-
|
|
215
|
-
|
|
212
|
+
const importLocation = componentCorePackage
|
|
213
|
+
? getPathToComponentTypes(config, outputTarget)
|
|
214
|
+
: normalizePath(componentsTypeFile);
|
|
216
215
|
const typeImports = `import ${isCustomElementsBuild ? 'type ' : ''}{ ${IMPORT_TYPES} } from '${importLocation}';\n`;
|
|
217
216
|
// defineCustomElement import
|
|
218
217
|
let sourceImport = '';
|
|
@@ -221,13 +220,10 @@ ${createImportStatement(['ProxyCmp'], './angular-component-lib/utils')}\n`;
|
|
|
221
220
|
}
|
|
222
221
|
// Generate component definition
|
|
223
222
|
const filterInternalProps = (prop) => !prop.internal;
|
|
224
|
-
const mapInputProp = (prop) => {
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
required: (_a = prop.required) !== null && _a !== void 0 ? _a : false,
|
|
229
|
-
});
|
|
230
|
-
};
|
|
223
|
+
const mapInputProp = (prop) => ({
|
|
224
|
+
name: prop.name,
|
|
225
|
+
required: prop.required ?? false,
|
|
226
|
+
});
|
|
231
227
|
const internalProps = [];
|
|
232
228
|
if (cmpMeta.properties) {
|
|
233
229
|
internalProps.push(...cmpMeta.properties.filter(filterInternalProps));
|
|
@@ -273,5 +269,21 @@ export function generateBarrelFile(components, outputTarget) {
|
|
|
273
269
|
.join('\n');
|
|
274
270
|
return header + exports + '\n';
|
|
275
271
|
}
|
|
272
|
+
export function getPathToComponentTypes(config, outputTarget) {
|
|
273
|
+
const basePkg = outputTarget.componentCorePackage || '';
|
|
274
|
+
// in v5, all types (including components.d.ts) are generated in the dist/types directory
|
|
275
|
+
const typesTarget = config.outputTargets?.find((o) => o.type === 'types');
|
|
276
|
+
if (typesTarget) {
|
|
277
|
+
const rawDir = typesTarget.dir || 'dist/types';
|
|
278
|
+
const relDir = config.rootDir && path.isAbsolute(rawDir) ? path.relative(config.rootDir, rawDir) : rawDir;
|
|
279
|
+
return normalizePath(path.join(basePkg, relDir, 'components'));
|
|
280
|
+
}
|
|
281
|
+
// v4: append customElementsDir for custom elements build, otherwise package root
|
|
282
|
+
const isCustomElementsBuild = isOutputTypeCustomElementsBuild(outputTarget.outputType);
|
|
283
|
+
if (isCustomElementsBuild && outputTarget.customElementsDir) {
|
|
284
|
+
return normalizePath(path.join(basePkg, outputTarget.customElementsDir));
|
|
285
|
+
}
|
|
286
|
+
return normalizePath(basePkg);
|
|
287
|
+
}
|
|
276
288
|
const GENERATED_DTS = 'components.d.ts';
|
|
277
289
|
const IMPORT_TYPES = 'Components';
|
package/dist/plugin.js
CHANGED
|
@@ -17,8 +17,13 @@ export const angularOutputTarget = (outputTarget) => {
|
|
|
17
17
|
};
|
|
18
18
|
};
|
|
19
19
|
export function normalizeOutputTarget(config, outputTarget) {
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
const results = {
|
|
21
|
+
...outputTarget,
|
|
22
|
+
excludeComponents: outputTarget.excludeComponents || [],
|
|
23
|
+
valueAccessorConfigs: outputTarget.valueAccessorConfigs || [],
|
|
24
|
+
customElementsDir: outputTarget.customElementsDir ?? 'components',
|
|
25
|
+
outputType: outputTarget.outputType ?? OutputTypes.Standalone,
|
|
26
|
+
};
|
|
22
27
|
if (config.rootDir == null) {
|
|
23
28
|
throw new Error('rootDir is not set and it should be set by stencil itself');
|
|
24
29
|
}
|
package/dist/utils.js
CHANGED
|
@@ -62,11 +62,10 @@ export function isRelativePath(path) {
|
|
|
62
62
|
return path && path.startsWith('.');
|
|
63
63
|
}
|
|
64
64
|
export async function readPackageJson(config, rootDir) {
|
|
65
|
-
var _a;
|
|
66
65
|
const pkgJsonPath = path.join(rootDir, 'package.json');
|
|
67
66
|
let pkgJson;
|
|
68
67
|
try {
|
|
69
|
-
pkgJson = (await
|
|
68
|
+
pkgJson = (await config.sys?.readFile(pkgJsonPath, 'utf8'));
|
|
70
69
|
}
|
|
71
70
|
catch (e) {
|
|
72
71
|
throw new Error(`Missing "package.json" file for distribution: ${pkgJsonPath}`);
|
|
@@ -120,6 +119,14 @@ export const createComponentEventTypeImports = (componentTagName, events, option
|
|
|
120
119
|
const namedImports = new Set();
|
|
121
120
|
const isCustomElementsBuild = isOutputTypeCustomElementsBuild(options.outputType);
|
|
122
121
|
const importPathName = normalizePath(componentCorePackage) + (isCustomElementsBuild ? `/${customElementsDir}` : '');
|
|
122
|
+
/**
|
|
123
|
+
* Each component's events are typed with the per-component CustomEvent type
|
|
124
|
+
* generated by Stencil (e.g. `MyComponentCustomEvent<Detail>`), which carries a
|
|
125
|
+
* narrowed `target`. Import it once per component that emits public events.
|
|
126
|
+
*/
|
|
127
|
+
if (events.length > 0) {
|
|
128
|
+
imports.push(`import type { ${componentTagName}CustomEvent } from '${importPathName}';`);
|
|
129
|
+
}
|
|
123
130
|
events.forEach((event) => {
|
|
124
131
|
Object.entries(event.complexType.references).forEach(([typeName, refObject]) => {
|
|
125
132
|
if (refObject.location === 'local' || refObject.location === 'import') {
|
package/dist/wizard.d.ts
ADDED
package/dist/wizard.js
ADDED
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
import { Project, SyntaxKind } from 'ts-morph';
|
|
2
|
+
import { access, mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { join, relative, dirname } from 'node:path';
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
// ts-morph helpers
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
function amendStencilConfig(configPath, targetCode) {
|
|
8
|
+
const project = new Project({ skipAddingFilesFromTsConfig: true });
|
|
9
|
+
const src = project.addSourceFileAtPath(configPath);
|
|
10
|
+
if (!src.getImportDeclaration('@stencil/angular-output-target')) {
|
|
11
|
+
src.addImportDeclaration({
|
|
12
|
+
moduleSpecifier: '@stencil/angular-output-target',
|
|
13
|
+
namedImports: ['angularOutputTarget'],
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
const existingProp = src
|
|
17
|
+
.getDescendantsOfKind(SyntaxKind.PropertyAssignment)
|
|
18
|
+
.find((p) => p.getName() === 'outputTargets');
|
|
19
|
+
if (!existingProp) {
|
|
20
|
+
const configObj = src.getVariableDeclaration('config')?.getInitializerIfKind(SyntaxKind.ObjectLiteralExpression) ??
|
|
21
|
+
src
|
|
22
|
+
.getDescendantsOfKind(SyntaxKind.ObjectLiteralExpression)
|
|
23
|
+
.find((obj) => obj.getProperty('namespace') !== undefined);
|
|
24
|
+
if (!configObj)
|
|
25
|
+
throw new Error('Could not find Stencil config object in stencil.config.ts');
|
|
26
|
+
configObj.addPropertyAssignment({ name: 'outputTargets', initializer: '[]' });
|
|
27
|
+
}
|
|
28
|
+
const prop = src.getDescendantsOfKind(SyntaxKind.PropertyAssignment).find((p) => p.getName() === 'outputTargets');
|
|
29
|
+
const arr = prop.getInitializerIfKind(SyntaxKind.ArrayLiteralExpression);
|
|
30
|
+
if (!arr)
|
|
31
|
+
throw new Error('outputTargets is not an array literal in stencil.config.ts');
|
|
32
|
+
const existing = arr.getText();
|
|
33
|
+
const alreadyAdded = existing.includes('angularOutputTarget(');
|
|
34
|
+
// Filter out any existing angularOutputTarget call so it can be replaced cleanly
|
|
35
|
+
const elements = arr
|
|
36
|
+
.getElements()
|
|
37
|
+
.map((e) => e.getText().trim())
|
|
38
|
+
.filter((e) => !e.includes('angularOutputTarget('));
|
|
39
|
+
const hasStandalone = existing.includes("type: 'standalone'") ||
|
|
40
|
+
existing.includes('type: "standalone"') ||
|
|
41
|
+
existing.includes("type: 'dist-custom-elements'") ||
|
|
42
|
+
existing.includes('type: "dist-custom-elements"');
|
|
43
|
+
if (!hasStandalone)
|
|
44
|
+
elements.push("{ type: 'standalone' }");
|
|
45
|
+
elements.push(targetCode);
|
|
46
|
+
prop.setInitializer(`[\n${elements.map((e) => ` ${e}`).join(',\n')},\n]`);
|
|
47
|
+
src.formatText();
|
|
48
|
+
src.saveSync();
|
|
49
|
+
return !alreadyAdded;
|
|
50
|
+
}
|
|
51
|
+
// ---------------------------------------------------------------------------
|
|
52
|
+
// pnpm build approval
|
|
53
|
+
// ---------------------------------------------------------------------------
|
|
54
|
+
async function allowPnpmBuild(pkg, rootDir, workspaceRoot) {
|
|
55
|
+
const base = workspaceRoot ?? rootDir;
|
|
56
|
+
const workspaceYaml = join(base, 'pnpm-workspace.yaml');
|
|
57
|
+
if (await pathExists(workspaceYaml)) {
|
|
58
|
+
let content = await readFile(workspaceYaml, 'utf8');
|
|
59
|
+
// onlyBuiltDependencies (list form)
|
|
60
|
+
if (!content.includes(`- '${pkg}'`) && !content.includes(`- "${pkg}"`) && !content.includes(`- ${pkg}\n`)) {
|
|
61
|
+
if (content.includes('onlyBuiltDependencies:')) {
|
|
62
|
+
content = content.replace(/^(onlyBuiltDependencies:[ \t]*\n)/m, `$1 - ${pkg}\n`);
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
content += `\nonlyBuiltDependencies:\n - ${pkg}\n`;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// allowBuilds (map form, pnpm 11+)
|
|
69
|
+
if (!content.includes(`${pkg}: true`)) {
|
|
70
|
+
if (content.includes('allowBuilds:')) {
|
|
71
|
+
content = content.replace(/^(allowBuilds:[ \t]*\n)/m, `$1 ${pkg}: true\n`);
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
content += `\nallowBuilds:\n ${pkg}: true\n`;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
await writeFile(workspaceYaml, content, 'utf8');
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
// Non-workspace: amend root package.json pnpm field
|
|
81
|
+
const rootPkgPath = join(base, 'package.json');
|
|
82
|
+
try {
|
|
83
|
+
const rootPkg = JSON.parse(await readFile(rootPkgPath, 'utf8'));
|
|
84
|
+
const existingList = rootPkg?.pnpm?.onlyBuiltDependencies ?? [];
|
|
85
|
+
const existingMap = rootPkg?.pnpm?.allowBuilds ?? {};
|
|
86
|
+
rootPkg.pnpm = {
|
|
87
|
+
...rootPkg.pnpm,
|
|
88
|
+
onlyBuiltDependencies: existingList.includes(pkg) ? existingList : [...existingList, pkg],
|
|
89
|
+
allowBuilds: { ...existingMap, [pkg]: true },
|
|
90
|
+
};
|
|
91
|
+
await writeFile(rootPkgPath, JSON.stringify(rootPkg, null, 2) + '\n', 'utf8');
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
// no root package.json — nothing to amend
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// ---------------------------------------------------------------------------
|
|
98
|
+
// Wrapper package scaffolding
|
|
99
|
+
// ---------------------------------------------------------------------------
|
|
100
|
+
async function pathExists(p) {
|
|
101
|
+
try {
|
|
102
|
+
await access(p);
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
function toPascalCase(s) {
|
|
110
|
+
return s
|
|
111
|
+
.split(/[-_]/)
|
|
112
|
+
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
|
113
|
+
.join('');
|
|
114
|
+
}
|
|
115
|
+
async function scaffoldWrapperPackage(wrapperDir, packageName, corePackageName, corePkgVersion, namespace, outputType) {
|
|
116
|
+
const libDir = join(wrapperDir, 'src', 'lib');
|
|
117
|
+
await mkdir(libDir, { recursive: true });
|
|
118
|
+
const className = toPascalCase(namespace);
|
|
119
|
+
const pkgJson = {
|
|
120
|
+
name: packageName,
|
|
121
|
+
version: '0.0.1',
|
|
122
|
+
scripts: { build: 'ng-packagr -p ng-package.json' },
|
|
123
|
+
peerDependencies: {
|
|
124
|
+
'@angular/common': '>=19',
|
|
125
|
+
'@angular/core': '>=19',
|
|
126
|
+
},
|
|
127
|
+
dependencies: { [corePackageName]: corePkgVersion },
|
|
128
|
+
devDependencies: {
|
|
129
|
+
'@angular/common': '>=19',
|
|
130
|
+
'@angular/core': '>=19',
|
|
131
|
+
'@angular/compiler': '>=19',
|
|
132
|
+
'@angular/compiler-cli': '>=19',
|
|
133
|
+
'ng-packagr': '>=19',
|
|
134
|
+
rxjs: '>=6',
|
|
135
|
+
tslib: '>=2',
|
|
136
|
+
typescript: '>=4 <7',
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
const ngPackage = {
|
|
140
|
+
lib: { entryFile: 'src/index.ts' },
|
|
141
|
+
deleteDestPath: false,
|
|
142
|
+
allowedNonPeerDependencies: [corePackageName],
|
|
143
|
+
};
|
|
144
|
+
const tsConfig = {
|
|
145
|
+
compilerOptions: {
|
|
146
|
+
target: 'ES2022',
|
|
147
|
+
module: 'ES2022',
|
|
148
|
+
moduleResolution: 'bundler',
|
|
149
|
+
experimentalDecorators: true,
|
|
150
|
+
strict: true,
|
|
151
|
+
skipLibCheck: true,
|
|
152
|
+
},
|
|
153
|
+
include: ['src/**/*.ts'],
|
|
154
|
+
};
|
|
155
|
+
const tsConfigLib = {
|
|
156
|
+
extends: './tsconfig.json',
|
|
157
|
+
compilerOptions: { declaration: false, inlineSources: true },
|
|
158
|
+
angularCompilerOptions: { compilationMode: 'partial' },
|
|
159
|
+
};
|
|
160
|
+
// NgModule file — only needed for 'component' outputType
|
|
161
|
+
let ngModuleTs;
|
|
162
|
+
if (outputType === 'component') {
|
|
163
|
+
ngModuleTs =
|
|
164
|
+
`import { NgModule } from '@angular/core';\n` +
|
|
165
|
+
`import { DIRECTIVES } from './directives.array';\n\n` +
|
|
166
|
+
`@NgModule({\n` +
|
|
167
|
+
` declarations: [...DIRECTIVES],\n` +
|
|
168
|
+
` exports: [...DIRECTIVES],\n` +
|
|
169
|
+
`})\n` +
|
|
170
|
+
`export class ${className}Module {}\n`;
|
|
171
|
+
}
|
|
172
|
+
// src/index.ts — public API surface
|
|
173
|
+
// directives.ts (the directivesProxyFile) sits at src/lib/directives.ts
|
|
174
|
+
let indexTs = `// Public API — re-exports generated by @stencil/angular-output-target.\n`;
|
|
175
|
+
indexTs += `export * from './lib/directives';\n`;
|
|
176
|
+
if (outputType === 'component') {
|
|
177
|
+
indexTs += `export * from './lib/${namespace}.module';\n`;
|
|
178
|
+
}
|
|
179
|
+
await Promise.all([
|
|
180
|
+
writeFile(join(wrapperDir, '.gitignore'), 'dist/\nnode_modules/\n.angular/\n', 'utf8'),
|
|
181
|
+
writeFile(join(wrapperDir, 'package.json'), JSON.stringify(pkgJson, null, 2) + '\n', 'utf8'),
|
|
182
|
+
writeFile(join(wrapperDir, 'ng-package.json'), JSON.stringify(ngPackage, null, 2) + '\n', 'utf8'),
|
|
183
|
+
writeFile(join(wrapperDir, 'tsconfig.json'), JSON.stringify(tsConfig, null, 2) + '\n', 'utf8'),
|
|
184
|
+
writeFile(join(wrapperDir, 'tsconfig.lib.json'), JSON.stringify(tsConfigLib, null, 2) + '\n', 'utf8'),
|
|
185
|
+
writeFile(join(wrapperDir, 'src', 'index.ts'), indexTs, 'utf8'),
|
|
186
|
+
...(ngModuleTs ? [writeFile(join(libDir, `${namespace}.module.ts`), ngModuleTs, 'utf8')] : []),
|
|
187
|
+
]);
|
|
188
|
+
}
|
|
189
|
+
// ---------------------------------------------------------------------------
|
|
190
|
+
// Wizard
|
|
191
|
+
// ---------------------------------------------------------------------------
|
|
192
|
+
export const wizard = {
|
|
193
|
+
init: {
|
|
194
|
+
id: '@stencil/angular-output-target',
|
|
195
|
+
displayName: 'Angular',
|
|
196
|
+
description: 'Angular component wrappers for your Stencil components',
|
|
197
|
+
async run({ config, workspaceRoot, prompts, nypm }) {
|
|
198
|
+
const { intro, outro, text, select, confirm, spinner, isCancel, cancel, log } = prompts;
|
|
199
|
+
intro('Angular output target');
|
|
200
|
+
const stencilConfigPath = join(config.rootDir, 'stencil.config.ts');
|
|
201
|
+
// Guard: already configured?
|
|
202
|
+
const guardProject = new Project({ skipAddingFilesFromTsConfig: true });
|
|
203
|
+
const alreadyConfigured = guardProject
|
|
204
|
+
.addSourceFileAtPath(stencilConfigPath)
|
|
205
|
+
.getDescendantsOfKind(SyntaxKind.PropertyAssignment)
|
|
206
|
+
.find((p) => p.getName() === 'outputTargets')
|
|
207
|
+
?.getInitializerIfKind(SyntaxKind.ArrayLiteralExpression)
|
|
208
|
+
?.getText()
|
|
209
|
+
.includes('angularOutputTarget(');
|
|
210
|
+
if (alreadyConfigured) {
|
|
211
|
+
const redo = await confirm({
|
|
212
|
+
message: 'angularOutputTarget is already in stencil.config.ts — reconfigure?',
|
|
213
|
+
initialValue: false,
|
|
214
|
+
});
|
|
215
|
+
if (isCancel(redo) || !redo) {
|
|
216
|
+
cancel('Skipping Angular setup.');
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
// Where should the wrapper package live?
|
|
221
|
+
let wrapperDir;
|
|
222
|
+
const defaultName = `${config.fsNamespace}-angular`;
|
|
223
|
+
if (workspaceRoot) {
|
|
224
|
+
const rawName = await text({
|
|
225
|
+
message: 'Wrapper package name?',
|
|
226
|
+
placeholder: defaultName,
|
|
227
|
+
defaultValue: defaultName,
|
|
228
|
+
});
|
|
229
|
+
if (isCancel(rawName)) {
|
|
230
|
+
cancel('Setup cancelled.');
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
wrapperDir = join(dirname(config.rootDir), rawName);
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
const defaultRel = `../${config.fsNamespace}-angular`;
|
|
237
|
+
const rawDir = await text({
|
|
238
|
+
message: 'Wrapper package directory? (relative to stencil.config.ts)',
|
|
239
|
+
placeholder: defaultRel,
|
|
240
|
+
defaultValue: defaultRel,
|
|
241
|
+
});
|
|
242
|
+
if (isCancel(rawDir)) {
|
|
243
|
+
cancel('Setup cancelled.');
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
wrapperDir = join(config.rootDir, rawDir);
|
|
247
|
+
}
|
|
248
|
+
// Angular component type — drives what we scaffold
|
|
249
|
+
const outputType = await select({
|
|
250
|
+
message: 'Angular component type?',
|
|
251
|
+
options: [
|
|
252
|
+
{ value: 'standalone', label: 'Standalone', hint: 'Modern Angular (v19+) — no NgModule' },
|
|
253
|
+
{ value: 'component', label: 'NgModule', hint: 'Classic — all components in one module' },
|
|
254
|
+
{ value: 'scam', label: 'SCAM', hint: 'One NgModule per component' },
|
|
255
|
+
],
|
|
256
|
+
});
|
|
257
|
+
if (isCancel(outputType)) {
|
|
258
|
+
cancel('Setup cancelled.');
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
const selectedOutputType = outputType;
|
|
262
|
+
// Derive stencil.config-relative paths from the wrapper location
|
|
263
|
+
const directivesProxyFile = relative(config.rootDir, join(wrapperDir, 'src', 'lib', 'directives.ts')).replace(/\\/g, '/');
|
|
264
|
+
const directivesArrayFile = selectedOutputType === 'component'
|
|
265
|
+
? relative(config.rootDir, join(wrapperDir, 'src', 'lib', 'directives.array.ts')).replace(/\\/g, '/')
|
|
266
|
+
: undefined;
|
|
267
|
+
const componentCorePackage = config.fsNamespace;
|
|
268
|
+
// Scaffold wrapper package if the directory doesn't exist yet
|
|
269
|
+
const shouldScaffold = !(await pathExists(wrapperDir));
|
|
270
|
+
if (shouldScaffold) {
|
|
271
|
+
const s = spinner();
|
|
272
|
+
s.start(`Scaffolding wrapper package at ${relative(workspaceRoot ?? config.rootDir, wrapperDir)}`);
|
|
273
|
+
try {
|
|
274
|
+
const corePkgVersion = workspaceRoot ? 'workspace:*' : `file:${relative(wrapperDir, config.rootDir)}`;
|
|
275
|
+
await scaffoldWrapperPackage(wrapperDir, `${config.fsNamespace}-angular`, componentCorePackage, corePkgVersion, config.fsNamespace, selectedOutputType);
|
|
276
|
+
s.stop('Wrapper package scaffolded');
|
|
277
|
+
}
|
|
278
|
+
catch (e) {
|
|
279
|
+
s.stop('Scaffolding failed — continuing');
|
|
280
|
+
log.warn(`Could not scaffold wrapper package: ${e}`);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
// Ensure esbuild (used by ng-packagr) is allowed to run install scripts in pnpm workspaces
|
|
284
|
+
try {
|
|
285
|
+
await allowPnpmBuild('esbuild', config.rootDir, workspaceRoot);
|
|
286
|
+
}
|
|
287
|
+
catch {
|
|
288
|
+
// non-fatal — user can run `pnpm approve-builds` manually
|
|
289
|
+
}
|
|
290
|
+
// Derive customElementsDir from an existing standalone target, or use the v5 default
|
|
291
|
+
const standaloneTarget = config.outputTargets?.find((o) => o.type === 'standalone');
|
|
292
|
+
const customElementsDir = standaloneTarget?.dir
|
|
293
|
+
? relative(config.rootDir, standaloneTarget.dir).replace(/\\/g, '/')
|
|
294
|
+
: 'dist/standalone';
|
|
295
|
+
// Build target config
|
|
296
|
+
const lines = [
|
|
297
|
+
`componentCorePackage: '${componentCorePackage}'`,
|
|
298
|
+
`customElementsDir: '${customElementsDir}'`,
|
|
299
|
+
`directivesProxyFile: '${directivesProxyFile}'`,
|
|
300
|
+
...(directivesArrayFile ? [`directivesArrayFile: '${directivesArrayFile}'`] : []),
|
|
301
|
+
...(selectedOutputType !== 'standalone' ? [`outputType: '${selectedOutputType}'`] : []),
|
|
302
|
+
];
|
|
303
|
+
const targetCode = `angularOutputTarget({\n ${lines.join(',\n ')},\n})`;
|
|
304
|
+
// Amend stencil.config.ts in one pass so formatText() sees the final array
|
|
305
|
+
try {
|
|
306
|
+
const added = amendStencilConfig(stencilConfigPath, targetCode);
|
|
307
|
+
log.success(added ? 'stencil.config.ts updated' : 'angularOutputTarget already present — no changes made');
|
|
308
|
+
}
|
|
309
|
+
catch (e) {
|
|
310
|
+
log.warn(`Could not automatically update stencil.config.ts (${e}). Add manually:\n\n` +
|
|
311
|
+
`import { angularOutputTarget } from '@stencil/angular-output-target';\n` +
|
|
312
|
+
`// in outputTargets:\n${targetCode}`);
|
|
313
|
+
}
|
|
314
|
+
// Install @stencil/angular-output-target as a devDep of the core package
|
|
315
|
+
log.info('Installing @stencil/angular-output-target...');
|
|
316
|
+
await nypm.addDependency(['@stencil/angular-output-target'], { cwd: config.rootDir, dev: true });
|
|
317
|
+
log.success('Installed @stencil/angular-output-target');
|
|
318
|
+
if (shouldScaffold && (await pathExists(wrapperDir))) {
|
|
319
|
+
log.info('Installing Angular dependencies in wrapper package...');
|
|
320
|
+
await nypm.addDependency([
|
|
321
|
+
'@angular/core',
|
|
322
|
+
'@angular/common',
|
|
323
|
+
'@angular/compiler',
|
|
324
|
+
'@angular/compiler-cli',
|
|
325
|
+
'ng-packagr',
|
|
326
|
+
'rxjs',
|
|
327
|
+
'tslib',
|
|
328
|
+
], { cwd: wrapperDir, dev: true });
|
|
329
|
+
log.success('Angular dependencies installed');
|
|
330
|
+
}
|
|
331
|
+
outro('Angular output target configured');
|
|
332
|
+
},
|
|
333
|
+
},
|
|
334
|
+
};
|
package/package.json
CHANGED
|
@@ -1,14 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stencil/angular-output-target",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Angular output target for @stencil/core components.",
|
|
5
|
-
"main": "dist/index.cjs
|
|
5
|
+
"main": "dist/index.cjs",
|
|
6
6
|
"module": "dist/index.js",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"stencil": {
|
|
9
|
+
"wizard": "./dist/wizard.js"
|
|
10
|
+
},
|
|
7
11
|
"exports": {
|
|
8
12
|
".": {
|
|
9
13
|
"types": "./dist/index.d.ts",
|
|
10
14
|
"import": "./dist/index.js",
|
|
11
|
-
"require": "./dist/index.cjs
|
|
15
|
+
"require": "./dist/index.cjs"
|
|
16
|
+
},
|
|
17
|
+
"./wizard": {
|
|
18
|
+
"types": "./dist/wizard.d.ts",
|
|
19
|
+
"import": "./dist/wizard.js"
|
|
12
20
|
}
|
|
13
21
|
},
|
|
14
22
|
"types": "dist/index.d.ts",
|
|
@@ -47,7 +55,11 @@
|
|
|
47
55
|
"bugs": {
|
|
48
56
|
"url": "https://github.com/stenciljs/output-targets/issues"
|
|
49
57
|
},
|
|
58
|
+
"dependencies": {
|
|
59
|
+
"ts-morph": "^28.0.0"
|
|
60
|
+
},
|
|
50
61
|
"devDependencies": {
|
|
62
|
+
"@stencil/cli": "^5.0.0 || >=5.0.0-alpha.0 <5.0.0-next.0",
|
|
51
63
|
"@angular/core": "8.2.14",
|
|
52
64
|
"@angular/forms": "8.2.14",
|
|
53
65
|
"@stencil/core": "4.41.0",
|
|
@@ -59,7 +71,7 @@
|
|
|
59
71
|
"vitest": "^2.1.4"
|
|
60
72
|
},
|
|
61
73
|
"peerDependencies": {
|
|
62
|
-
"@stencil/core": ">=2.0.0 || >=3 || >=
|
|
74
|
+
"@stencil/core": ">=2.0.0 || >=3 || >=4.0.0 || ^5.0.0 || >=5.0.0-alpha.0 <5.0.0-next.0"
|
|
63
75
|
},
|
|
64
76
|
"gitHead": "a3588e905186a0e86e7f88418fd5b2f9531b55e0"
|
|
65
77
|
}
|