@stencil/angular-output-target 0.9.1 → 0.10.1
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/README.md +1 -0
- package/dist/generate-angular-component.d.ts +3 -2
- package/dist/generate-angular-component.js +31 -13
- package/dist/index.cjs.js +53 -39
- package/dist/index.js +36 -16
- package/dist/output-angular.js +5 -3
- package/dist/types.d.ts +6 -0
- package/package.json +12 -20
- package/resources/control-value-accessors/boolean-value-accessor.ts +1 -1
- package/resources/control-value-accessors/number-value-accessor.ts +1 -1
package/README.md
CHANGED
|
@@ -49,3 +49,4 @@ export const config: Config = {
|
|
|
49
49
|
| `excludeComponents` | An array of tag names to exclude from generating component wrappers for. This is helpful when have a custom framework implementation of a specific component or need to extend the base component wrapper behavior. |
|
|
50
50
|
| `outputType` | Specifies the type of output to be generated. It can take one of the following values: <br />1. `component`: Generates all the component wrappers to be declared on an Angular module. This option is required for Stencil projects using the `dist` hydrated output.<br /> 2. `scam`: Generates a separate Angular module for each component.<br /> 3. `standalone`: Generates standalone component wrappers.<br /> Both `scam` and `standalone` options are compatible with the `dist-custom-elements` output. <br />Note: Please choose the appropriate `outputType` based on your project's requirements and the desired output structure. Defaults to `component`. |
|
|
51
51
|
| `customElementsDir` | This is the directory where the custom elements are imported from when using the [Custom Elements Bundle](https://stenciljs.com/docs/custom-elements). Defaults to the `components` directory. Only applies for `outputType: "scam"` or `outputType: "standalone"`. |
|
|
52
|
+
| `inlineProperties` | Experimental. When true, tries to inline the properties of components. This is required to enable Angular Language Service to type-check and show jsdocs when using the components in html-templates. |
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ComponentCompilerEvent } from '@stencil/core/internal';
|
|
1
|
+
import type { ComponentCompilerEvent, ComponentCompilerProperty } from '@stencil/core/internal';
|
|
2
2
|
import type { OutputType } from './types';
|
|
3
3
|
/**
|
|
4
4
|
* Creates an Angular component declaration from formatted Stencil compiler metadata.
|
|
@@ -9,9 +9,10 @@ import type { OutputType } from './types';
|
|
|
9
9
|
* @param methods The methods of the Stencil component. (e.g. ['myMethod']).
|
|
10
10
|
* @param includeImportCustomElements Whether to define the component as a custom element.
|
|
11
11
|
* @param standalone Whether to define the component as a standalone component.
|
|
12
|
+
* @param inlineComponentProps List of properties that should be inlined into the component definition.
|
|
12
13
|
* @returns The component declaration as a string.
|
|
13
14
|
*/
|
|
14
|
-
export declare const createAngularComponentDefinition: (tagName: string, inputs: readonly string[], outputs: readonly string[], methods: readonly string[], includeImportCustomElements?: boolean, standalone?: boolean) => string;
|
|
15
|
+
export declare const createAngularComponentDefinition: (tagName: string, inputs: readonly string[], outputs: readonly string[], methods: readonly string[], includeImportCustomElements?: boolean, standalone?: boolean, inlineComponentProps?: readonly ComponentCompilerProperty[]) => string;
|
|
15
16
|
/**
|
|
16
17
|
* Creates the component interface type definition.
|
|
17
18
|
* @param outputType The output type.
|
|
@@ -1,4 +1,29 @@
|
|
|
1
1
|
import { createComponentEventTypeImports, dashToPascalCase, formatToQuotedList } from './utils';
|
|
2
|
+
/**
|
|
3
|
+
* Creates a property declaration.
|
|
4
|
+
*
|
|
5
|
+
* @param prop A ComponentCompilerEvent or ComponentCompilerProperty to turn into a property declaration.
|
|
6
|
+
* @param type The name of the type (e.g. 'string')
|
|
7
|
+
* @param inlinePropertyAsSetter Inlines the entire property as an empty Setter, to aid Angulars Compilerp
|
|
8
|
+
* @returns The property declaration as a string.
|
|
9
|
+
*/
|
|
10
|
+
function createPropertyDeclaration(prop, type, inlinePropertyAsSetter = false) {
|
|
11
|
+
const comment = createDocComment(prop.docs);
|
|
12
|
+
let eventName = prop.name;
|
|
13
|
+
if (/[-/]/.test(prop.name)) {
|
|
14
|
+
// If a member name includes a dash or a forward slash, we need to wrap it in quotes.
|
|
15
|
+
// https://github.com/ionic-team/stencil-ds-output-targets/issues/212
|
|
16
|
+
eventName = `'${prop.name}'`;
|
|
17
|
+
}
|
|
18
|
+
if (inlinePropertyAsSetter) {
|
|
19
|
+
return `${comment.length > 0 ? ` ${comment}` : ''}
|
|
20
|
+
set ${eventName}(_: ${type}) {};`;
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
return `${comment.length > 0 ? ` ${comment}` : ''}
|
|
24
|
+
${eventName}: ${type};`;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
2
27
|
/**
|
|
3
28
|
* Creates an Angular component declaration from formatted Stencil compiler metadata.
|
|
4
29
|
*
|
|
@@ -8,9 +33,10 @@ import { createComponentEventTypeImports, dashToPascalCase, formatToQuotedList }
|
|
|
8
33
|
* @param methods The methods of the Stencil component. (e.g. ['myMethod']).
|
|
9
34
|
* @param includeImportCustomElements Whether to define the component as a custom element.
|
|
10
35
|
* @param standalone Whether to define the component as a standalone component.
|
|
36
|
+
* @param inlineComponentProps List of properties that should be inlined into the component definition.
|
|
11
37
|
* @returns The component declaration as a string.
|
|
12
38
|
*/
|
|
13
|
-
export const createAngularComponentDefinition = (tagName, inputs, outputs, methods, includeImportCustomElements = false, standalone = false) => {
|
|
39
|
+
export const createAngularComponentDefinition = (tagName, inputs, outputs, methods, includeImportCustomElements = false, standalone = false, inlineComponentProps = []) => {
|
|
14
40
|
const tagNameAsPascal = dashToPascalCase(tagName);
|
|
15
41
|
const hasInputs = inputs.length > 0;
|
|
16
42
|
const hasOutputs = outputs.length > 0;
|
|
@@ -36,6 +62,8 @@ export const createAngularComponentDefinition = (tagName, inputs, outputs, metho
|
|
|
36
62
|
if (standalone && includeImportCustomElements) {
|
|
37
63
|
standaloneOption = `\n standalone: true`;
|
|
38
64
|
}
|
|
65
|
+
const propertyDeclarations = inlineComponentProps.map((m) => createPropertyDeclaration(m, `Components.${tagNameAsPascal}['${m.name}']`, true));
|
|
66
|
+
const propertiesDeclarationText = [`protected el: HTML${tagNameAsPascal}Element;`, ...propertyDeclarations].join('\n ');
|
|
39
67
|
/**
|
|
40
68
|
* Notes on the generated output:
|
|
41
69
|
* - We disable @angular-eslint/no-inputs-metadata-property, so that
|
|
@@ -52,7 +80,7 @@ export const createAngularComponentDefinition = (tagName, inputs, outputs, metho
|
|
|
52
80
|
inputs: [${formattedInputs}],${standaloneOption}
|
|
53
81
|
})
|
|
54
82
|
export class ${tagNameAsPascal} {
|
|
55
|
-
|
|
83
|
+
${propertiesDeclarationText}
|
|
56
84
|
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
|
|
57
85
|
c.detach();
|
|
58
86
|
this.el = r.nativeElement;${hasOutputs
|
|
@@ -146,17 +174,7 @@ export const createComponentTypeDefinition = (outputType, tagNameAsPascal, event
|
|
|
146
174
|
customElementsDir,
|
|
147
175
|
outputType,
|
|
148
176
|
});
|
|
149
|
-
const eventTypes = publicEvents.map((event) => {
|
|
150
|
-
const comment = createDocComment(event.docs);
|
|
151
|
-
let eventName = event.name;
|
|
152
|
-
if (/[-/]/.test(event.name)) {
|
|
153
|
-
// If an event name includes a dash or a forward slash, we need to wrap it in quotes.
|
|
154
|
-
// https://github.com/ionic-team/stencil-ds-output-targets/issues/212
|
|
155
|
-
eventName = `'${event.name}'`;
|
|
156
|
-
}
|
|
157
|
-
return `${comment.length > 0 ? ` ${comment}` : ''}
|
|
158
|
-
${eventName}: EventEmitter<CustomEvent<${formatOutputType(tagNameAsPascal, event)}>>;`;
|
|
159
|
-
});
|
|
177
|
+
const eventTypes = publicEvents.map((event) => createPropertyDeclaration(event, `EventEmitter<CustomEvent<${formatOutputType(tagNameAsPascal, event)}>>`));
|
|
160
178
|
const interfaceDeclaration = `export declare interface ${tagNameAsPascal} extends Components.${tagNameAsPascal} {`;
|
|
161
179
|
const typeDefinition = (eventTypeImports.length > 0 ? `${eventTypeImports + '\n\n'}` : '') +
|
|
162
180
|
`${interfaceDeclaration}${eventTypes.length === 0
|
package/dist/index.cjs.js
CHANGED
|
@@ -1,14 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
-
|
|
5
3
|
var path = require('path');
|
|
6
4
|
var os = require('os');
|
|
7
5
|
|
|
8
|
-
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
9
|
-
|
|
10
|
-
var path__default = /*#__PURE__*/_interopDefaultLegacy(path);
|
|
11
|
-
|
|
12
6
|
const OutputTypes = {
|
|
13
7
|
Component: 'component',
|
|
14
8
|
Scam: 'scam',
|
|
@@ -58,18 +52,18 @@ function normalizePath(str) {
|
|
|
58
52
|
return str;
|
|
59
53
|
}
|
|
60
54
|
function relativeImport(pathFrom, pathTo, ext) {
|
|
61
|
-
let relativePath =
|
|
55
|
+
let relativePath = path.relative(path.dirname(pathFrom), path.dirname(pathTo));
|
|
62
56
|
if (relativePath === '') {
|
|
63
57
|
relativePath = '.';
|
|
64
58
|
}
|
|
65
59
|
else if (relativePath[0] !== '.') {
|
|
66
60
|
relativePath = './' + relativePath;
|
|
67
61
|
}
|
|
68
|
-
return normalizePath(`${relativePath}/${
|
|
62
|
+
return normalizePath(`${relativePath}/${path.basename(pathTo, ext)}`);
|
|
69
63
|
}
|
|
70
64
|
async function readPackageJson(config, rootDir) {
|
|
71
65
|
var _a;
|
|
72
|
-
const pkgJsonPath =
|
|
66
|
+
const pkgJsonPath = path.join(rootDir, 'package.json');
|
|
73
67
|
let pkgJson;
|
|
74
68
|
try {
|
|
75
69
|
pkgJson = (await ((_a = config.sys) === null || _a === void 0 ? void 0 : _a.readFile(pkgJsonPath, 'utf8')));
|
|
@@ -144,6 +138,31 @@ const EXTENDED_PATH_REGEX = /^\\\\\?\\/;
|
|
|
144
138
|
const NON_ASCII_REGEX = /[^\x00-\x80]+/;
|
|
145
139
|
const SLASH_REGEX = /\\/g;
|
|
146
140
|
|
|
141
|
+
/**
|
|
142
|
+
* Creates a property declaration.
|
|
143
|
+
*
|
|
144
|
+
* @param prop A ComponentCompilerEvent or ComponentCompilerProperty to turn into a property declaration.
|
|
145
|
+
* @param type The name of the type (e.g. 'string')
|
|
146
|
+
* @param inlinePropertyAsSetter Inlines the entire property as an empty Setter, to aid Angulars Compilerp
|
|
147
|
+
* @returns The property declaration as a string.
|
|
148
|
+
*/
|
|
149
|
+
function createPropertyDeclaration(prop, type, inlinePropertyAsSetter = false) {
|
|
150
|
+
const comment = createDocComment(prop.docs);
|
|
151
|
+
let eventName = prop.name;
|
|
152
|
+
if (/[-/]/.test(prop.name)) {
|
|
153
|
+
// If a member name includes a dash or a forward slash, we need to wrap it in quotes.
|
|
154
|
+
// https://github.com/ionic-team/stencil-ds-output-targets/issues/212
|
|
155
|
+
eventName = `'${prop.name}'`;
|
|
156
|
+
}
|
|
157
|
+
if (inlinePropertyAsSetter) {
|
|
158
|
+
return `${comment.length > 0 ? ` ${comment}` : ''}
|
|
159
|
+
set ${eventName}(_: ${type}) {};`;
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
return `${comment.length > 0 ? ` ${comment}` : ''}
|
|
163
|
+
${eventName}: ${type};`;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
147
166
|
/**
|
|
148
167
|
* Creates an Angular component declaration from formatted Stencil compiler metadata.
|
|
149
168
|
*
|
|
@@ -153,9 +172,10 @@ const SLASH_REGEX = /\\/g;
|
|
|
153
172
|
* @param methods The methods of the Stencil component. (e.g. ['myMethod']).
|
|
154
173
|
* @param includeImportCustomElements Whether to define the component as a custom element.
|
|
155
174
|
* @param standalone Whether to define the component as a standalone component.
|
|
175
|
+
* @param inlineComponentProps List of properties that should be inlined into the component definition.
|
|
156
176
|
* @returns The component declaration as a string.
|
|
157
177
|
*/
|
|
158
|
-
const createAngularComponentDefinition = (tagName, inputs, outputs, methods, includeImportCustomElements = false, standalone = false) => {
|
|
178
|
+
const createAngularComponentDefinition = (tagName, inputs, outputs, methods, includeImportCustomElements = false, standalone = false, inlineComponentProps = []) => {
|
|
159
179
|
const tagNameAsPascal = dashToPascalCase(tagName);
|
|
160
180
|
const hasInputs = inputs.length > 0;
|
|
161
181
|
const hasOutputs = outputs.length > 0;
|
|
@@ -181,6 +201,8 @@ const createAngularComponentDefinition = (tagName, inputs, outputs, methods, inc
|
|
|
181
201
|
if (standalone && includeImportCustomElements) {
|
|
182
202
|
standaloneOption = `\n standalone: true`;
|
|
183
203
|
}
|
|
204
|
+
const propertyDeclarations = inlineComponentProps.map((m) => createPropertyDeclaration(m, `Components.${tagNameAsPascal}['${m.name}']`, true));
|
|
205
|
+
const propertiesDeclarationText = [`protected el: HTML${tagNameAsPascal}Element;`, ...propertyDeclarations].join('\n ');
|
|
184
206
|
/**
|
|
185
207
|
* Notes on the generated output:
|
|
186
208
|
* - We disable @angular-eslint/no-inputs-metadata-property, so that
|
|
@@ -197,7 +219,7 @@ const createAngularComponentDefinition = (tagName, inputs, outputs, methods, inc
|
|
|
197
219
|
inputs: [${formattedInputs}],${standaloneOption}
|
|
198
220
|
})
|
|
199
221
|
export class ${tagNameAsPascal} {
|
|
200
|
-
|
|
222
|
+
${propertiesDeclarationText}
|
|
201
223
|
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
|
|
202
224
|
c.detach();
|
|
203
225
|
this.el = r.nativeElement;${hasOutputs
|
|
@@ -291,17 +313,7 @@ const createComponentTypeDefinition = (outputType, tagNameAsPascal, events, comp
|
|
|
291
313
|
customElementsDir,
|
|
292
314
|
outputType,
|
|
293
315
|
});
|
|
294
|
-
const eventTypes = publicEvents.map((event) => {
|
|
295
|
-
const comment = createDocComment(event.docs);
|
|
296
|
-
let eventName = event.name;
|
|
297
|
-
if (/[-/]/.test(event.name)) {
|
|
298
|
-
// If an event name includes a dash or a forward slash, we need to wrap it in quotes.
|
|
299
|
-
// https://github.com/ionic-team/stencil-ds-output-targets/issues/212
|
|
300
|
-
eventName = `'${event.name}'`;
|
|
301
|
-
}
|
|
302
|
-
return `${comment.length > 0 ? ` ${comment}` : ''}
|
|
303
|
-
${eventName}: EventEmitter<CustomEvent<${formatOutputType(tagNameAsPascal, event)}>>;`;
|
|
304
|
-
});
|
|
316
|
+
const eventTypes = publicEvents.map((event) => createPropertyDeclaration(event, `EventEmitter<CustomEvent<${formatOutputType(tagNameAsPascal, event)}>>`));
|
|
305
317
|
const interfaceDeclaration = `export declare interface ${tagNameAsPascal} extends Components.${tagNameAsPascal} {`;
|
|
306
318
|
const typeDefinition = (eventTypeImports.length > 0 ? `${eventTypeImports + '\n\n'}` : '') +
|
|
307
319
|
`${interfaceDeclaration}${eventTypes.length === 0
|
|
@@ -336,7 +348,7 @@ async function generateValueAccessors(compilerCtx, components, outputTarget, con
|
|
|
336
348
|
if (!Array.isArray(outputTarget.valueAccessorConfigs) || outputTarget.valueAccessorConfigs.length === 0) {
|
|
337
349
|
return;
|
|
338
350
|
}
|
|
339
|
-
const targetDir =
|
|
351
|
+
const targetDir = path.dirname(outputTarget.directivesProxyFile);
|
|
340
352
|
const normalizedValueAccessors = outputTarget.valueAccessorConfigs.reduce((allAccessors, va) => {
|
|
341
353
|
const elementSelectors = Array.isArray(va.elementSelectors) ? va.elementSelectors : [va.elementSelectors];
|
|
342
354
|
const type = va.type;
|
|
@@ -354,8 +366,8 @@ async function generateValueAccessors(compilerCtx, components, outputTarget, con
|
|
|
354
366
|
await Promise.all(Object.keys(normalizedValueAccessors).map(async (type) => {
|
|
355
367
|
const valueAccessorType = type; // Object.keys converts to string
|
|
356
368
|
const targetFileName = `${type}-value-accessor.ts`;
|
|
357
|
-
const targetFilePath =
|
|
358
|
-
const srcFilePath =
|
|
369
|
+
const targetFilePath = path.join(targetDir, targetFileName);
|
|
370
|
+
const srcFilePath = path.join(__dirname, '../resources/control-value-accessors/', targetFileName);
|
|
359
371
|
const srcFileContents = await compilerCtx.fs.readFile(srcFilePath);
|
|
360
372
|
const finalText = createValueAccessor(srcFileContents, normalizedValueAccessors[valueAccessorType], outputTarget.outputType);
|
|
361
373
|
await compilerCtx.fs.writeFile(targetFilePath, finalText);
|
|
@@ -375,14 +387,14 @@ function copyResources$1(config, resourcesFilesToCopy, directory) {
|
|
|
375
387
|
}
|
|
376
388
|
const copyTasks = resourcesFilesToCopy.map((rf) => {
|
|
377
389
|
return {
|
|
378
|
-
src:
|
|
379
|
-
dest:
|
|
390
|
+
src: path.join(__dirname, '../resources/control-value-accessors/', rf),
|
|
391
|
+
dest: path.join(directory, rf),
|
|
380
392
|
keepDirStructure: false,
|
|
381
393
|
warn: false,
|
|
382
394
|
ignore: [],
|
|
383
395
|
};
|
|
384
396
|
});
|
|
385
|
-
return config.sys.copy(copyTasks,
|
|
397
|
+
return config.sys.copy(copyTasks, path.join(directory));
|
|
386
398
|
}
|
|
387
399
|
const VALUE_ACCESSOR_SELECTORS = `<VALUE_ACCESSOR_SELECTORS>`;
|
|
388
400
|
const VALUE_ACCESSOR_EVENT = `<VALUE_ACCESSOR_EVENT>`;
|
|
@@ -426,8 +438,8 @@ async function copyResources(config, outputTarget) {
|
|
|
426
438
|
if (!config.sys || !config.sys.copy || !config.sys.glob) {
|
|
427
439
|
throw new Error('stencil is not properly initialized at this step. Notify the developer');
|
|
428
440
|
}
|
|
429
|
-
const srcDirectory =
|
|
430
|
-
const destDirectory =
|
|
441
|
+
const srcDirectory = path.join(__dirname, '..', 'angular-component-lib');
|
|
442
|
+
const destDirectory = path.join(path.dirname(outputTarget.directivesProxyFile), 'angular-component-lib');
|
|
431
443
|
return config.sys.copy([
|
|
432
444
|
{
|
|
433
445
|
src: srcDirectory,
|
|
@@ -439,8 +451,8 @@ async function copyResources(config, outputTarget) {
|
|
|
439
451
|
], srcDirectory);
|
|
440
452
|
}
|
|
441
453
|
function generateProxies(components, pkgData, outputTarget, rootDir) {
|
|
442
|
-
const distTypesDir =
|
|
443
|
-
const dtsFilePath =
|
|
454
|
+
const distTypesDir = path.dirname(pkgData.types);
|
|
455
|
+
const dtsFilePath = path.join(rootDir, distTypesDir, GENERATED_DTS);
|
|
444
456
|
const { outputType } = outputTarget;
|
|
445
457
|
const componentsTypeFile = relativeImport(outputTarget.directivesProxyFile, dtsFilePath, '.d.ts');
|
|
446
458
|
const includeSingleComponentAngularModules = outputType === OutputTypes.Scam;
|
|
@@ -504,10 +516,11 @@ ${createImportStatement(componentLibImports, './angular-component-lib/utils')}\n
|
|
|
504
516
|
const { componentCorePackage, customElementsDir } = outputTarget;
|
|
505
517
|
for (let cmpMeta of components) {
|
|
506
518
|
const tagNameAsPascal = dashToPascalCase(cmpMeta.tagName);
|
|
507
|
-
const
|
|
519
|
+
const internalProps = [];
|
|
508
520
|
if (cmpMeta.properties) {
|
|
509
|
-
|
|
521
|
+
internalProps.push(...cmpMeta.properties.filter(filterInternalProps));
|
|
510
522
|
}
|
|
523
|
+
const inputs = internalProps.map(mapPropName);
|
|
511
524
|
if (cmpMeta.virtualProperties) {
|
|
512
525
|
inputs.push(...cmpMeta.virtualProperties.map(mapPropName));
|
|
513
526
|
}
|
|
@@ -520,13 +533,14 @@ ${createImportStatement(componentLibImports, './angular-component-lib/utils')}\n
|
|
|
520
533
|
if (cmpMeta.methods) {
|
|
521
534
|
methods.push(...cmpMeta.methods.filter(filterInternalProps).map(mapPropName));
|
|
522
535
|
}
|
|
536
|
+
const inlineComponentProps = outputTarget.inlineProperties ? internalProps : [];
|
|
523
537
|
/**
|
|
524
538
|
* For each component, we need to generate:
|
|
525
539
|
* 1. The @Component decorated class
|
|
526
540
|
* 2. Optionally the @NgModule decorated class (if includeSingleComponentAngularModules is true)
|
|
527
541
|
* 3. The component interface (using declaration merging for types).
|
|
528
542
|
*/
|
|
529
|
-
const componentDefinition = createAngularComponentDefinition(cmpMeta.tagName, inputs, outputs, methods, isCustomElementsBuild, isStandaloneBuild);
|
|
543
|
+
const componentDefinition = createAngularComponentDefinition(cmpMeta.tagName, inputs, outputs, methods, isCustomElementsBuild, isStandaloneBuild, inlineComponentProps);
|
|
530
544
|
const moduleDefinition = generateAngularModuleForComponent(cmpMeta.tagName);
|
|
531
545
|
const componentTypeDefinition = createComponentTypeDefinition(outputType, tagNameAsPascal, cmpMeta.events, componentCorePackage, customElementsDir);
|
|
532
546
|
proxyFileOutput.push(componentDefinition, '\n');
|
|
@@ -564,11 +578,11 @@ function normalizeOutputTarget(config, outputTarget) {
|
|
|
564
578
|
if (outputTarget.directivesProxyFile == null) {
|
|
565
579
|
throw new Error('directivesProxyFile is required. Please set it in the Stencil config.');
|
|
566
580
|
}
|
|
567
|
-
if (outputTarget.directivesProxyFile && !
|
|
568
|
-
results.directivesProxyFile = normalizePath(
|
|
581
|
+
if (outputTarget.directivesProxyFile && !path.isAbsolute(outputTarget.directivesProxyFile)) {
|
|
582
|
+
results.directivesProxyFile = normalizePath(path.join(config.rootDir, outputTarget.directivesProxyFile));
|
|
569
583
|
}
|
|
570
|
-
if (outputTarget.directivesArrayFile && !
|
|
571
|
-
results.directivesArrayFile = normalizePath(
|
|
584
|
+
if (outputTarget.directivesArrayFile && !path.isAbsolute(outputTarget.directivesArrayFile)) {
|
|
585
|
+
results.directivesArrayFile = normalizePath(path.join(config.rootDir, outputTarget.directivesArrayFile));
|
|
572
586
|
}
|
|
573
587
|
if (outputTarget.includeSingleComponentAngularModules !== undefined) {
|
|
574
588
|
throw new Error("The 'includeSingleComponentAngularModules' option has been removed. Please use 'outputType' instead.");
|
package/dist/index.js
CHANGED
|
@@ -136,6 +136,31 @@ const EXTENDED_PATH_REGEX = /^\\\\\?\\/;
|
|
|
136
136
|
const NON_ASCII_REGEX = /[^\x00-\x80]+/;
|
|
137
137
|
const SLASH_REGEX = /\\/g;
|
|
138
138
|
|
|
139
|
+
/**
|
|
140
|
+
* Creates a property declaration.
|
|
141
|
+
*
|
|
142
|
+
* @param prop A ComponentCompilerEvent or ComponentCompilerProperty to turn into a property declaration.
|
|
143
|
+
* @param type The name of the type (e.g. 'string')
|
|
144
|
+
* @param inlinePropertyAsSetter Inlines the entire property as an empty Setter, to aid Angulars Compilerp
|
|
145
|
+
* @returns The property declaration as a string.
|
|
146
|
+
*/
|
|
147
|
+
function createPropertyDeclaration(prop, type, inlinePropertyAsSetter = false) {
|
|
148
|
+
const comment = createDocComment(prop.docs);
|
|
149
|
+
let eventName = prop.name;
|
|
150
|
+
if (/[-/]/.test(prop.name)) {
|
|
151
|
+
// If a member name includes a dash or a forward slash, we need to wrap it in quotes.
|
|
152
|
+
// https://github.com/ionic-team/stencil-ds-output-targets/issues/212
|
|
153
|
+
eventName = `'${prop.name}'`;
|
|
154
|
+
}
|
|
155
|
+
if (inlinePropertyAsSetter) {
|
|
156
|
+
return `${comment.length > 0 ? ` ${comment}` : ''}
|
|
157
|
+
set ${eventName}(_: ${type}) {};`;
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
return `${comment.length > 0 ? ` ${comment}` : ''}
|
|
161
|
+
${eventName}: ${type};`;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
139
164
|
/**
|
|
140
165
|
* Creates an Angular component declaration from formatted Stencil compiler metadata.
|
|
141
166
|
*
|
|
@@ -145,9 +170,10 @@ const SLASH_REGEX = /\\/g;
|
|
|
145
170
|
* @param methods The methods of the Stencil component. (e.g. ['myMethod']).
|
|
146
171
|
* @param includeImportCustomElements Whether to define the component as a custom element.
|
|
147
172
|
* @param standalone Whether to define the component as a standalone component.
|
|
173
|
+
* @param inlineComponentProps List of properties that should be inlined into the component definition.
|
|
148
174
|
* @returns The component declaration as a string.
|
|
149
175
|
*/
|
|
150
|
-
const createAngularComponentDefinition = (tagName, inputs, outputs, methods, includeImportCustomElements = false, standalone = false) => {
|
|
176
|
+
const createAngularComponentDefinition = (tagName, inputs, outputs, methods, includeImportCustomElements = false, standalone = false, inlineComponentProps = []) => {
|
|
151
177
|
const tagNameAsPascal = dashToPascalCase(tagName);
|
|
152
178
|
const hasInputs = inputs.length > 0;
|
|
153
179
|
const hasOutputs = outputs.length > 0;
|
|
@@ -173,6 +199,8 @@ const createAngularComponentDefinition = (tagName, inputs, outputs, methods, inc
|
|
|
173
199
|
if (standalone && includeImportCustomElements) {
|
|
174
200
|
standaloneOption = `\n standalone: true`;
|
|
175
201
|
}
|
|
202
|
+
const propertyDeclarations = inlineComponentProps.map((m) => createPropertyDeclaration(m, `Components.${tagNameAsPascal}['${m.name}']`, true));
|
|
203
|
+
const propertiesDeclarationText = [`protected el: HTML${tagNameAsPascal}Element;`, ...propertyDeclarations].join('\n ');
|
|
176
204
|
/**
|
|
177
205
|
* Notes on the generated output:
|
|
178
206
|
* - We disable @angular-eslint/no-inputs-metadata-property, so that
|
|
@@ -189,7 +217,7 @@ const createAngularComponentDefinition = (tagName, inputs, outputs, methods, inc
|
|
|
189
217
|
inputs: [${formattedInputs}],${standaloneOption}
|
|
190
218
|
})
|
|
191
219
|
export class ${tagNameAsPascal} {
|
|
192
|
-
|
|
220
|
+
${propertiesDeclarationText}
|
|
193
221
|
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
|
|
194
222
|
c.detach();
|
|
195
223
|
this.el = r.nativeElement;${hasOutputs
|
|
@@ -283,17 +311,7 @@ const createComponentTypeDefinition = (outputType, tagNameAsPascal, events, comp
|
|
|
283
311
|
customElementsDir,
|
|
284
312
|
outputType,
|
|
285
313
|
});
|
|
286
|
-
const eventTypes = publicEvents.map((event) => {
|
|
287
|
-
const comment = createDocComment(event.docs);
|
|
288
|
-
let eventName = event.name;
|
|
289
|
-
if (/[-/]/.test(event.name)) {
|
|
290
|
-
// If an event name includes a dash or a forward slash, we need to wrap it in quotes.
|
|
291
|
-
// https://github.com/ionic-team/stencil-ds-output-targets/issues/212
|
|
292
|
-
eventName = `'${event.name}'`;
|
|
293
|
-
}
|
|
294
|
-
return `${comment.length > 0 ? ` ${comment}` : ''}
|
|
295
|
-
${eventName}: EventEmitter<CustomEvent<${formatOutputType(tagNameAsPascal, event)}>>;`;
|
|
296
|
-
});
|
|
314
|
+
const eventTypes = publicEvents.map((event) => createPropertyDeclaration(event, `EventEmitter<CustomEvent<${formatOutputType(tagNameAsPascal, event)}>>`));
|
|
297
315
|
const interfaceDeclaration = `export declare interface ${tagNameAsPascal} extends Components.${tagNameAsPascal} {`;
|
|
298
316
|
const typeDefinition = (eventTypeImports.length > 0 ? `${eventTypeImports + '\n\n'}` : '') +
|
|
299
317
|
`${interfaceDeclaration}${eventTypes.length === 0
|
|
@@ -496,10 +514,11 @@ ${createImportStatement(componentLibImports, './angular-component-lib/utils')}\n
|
|
|
496
514
|
const { componentCorePackage, customElementsDir } = outputTarget;
|
|
497
515
|
for (let cmpMeta of components) {
|
|
498
516
|
const tagNameAsPascal = dashToPascalCase(cmpMeta.tagName);
|
|
499
|
-
const
|
|
517
|
+
const internalProps = [];
|
|
500
518
|
if (cmpMeta.properties) {
|
|
501
|
-
|
|
519
|
+
internalProps.push(...cmpMeta.properties.filter(filterInternalProps));
|
|
502
520
|
}
|
|
521
|
+
const inputs = internalProps.map(mapPropName);
|
|
503
522
|
if (cmpMeta.virtualProperties) {
|
|
504
523
|
inputs.push(...cmpMeta.virtualProperties.map(mapPropName));
|
|
505
524
|
}
|
|
@@ -512,13 +531,14 @@ ${createImportStatement(componentLibImports, './angular-component-lib/utils')}\n
|
|
|
512
531
|
if (cmpMeta.methods) {
|
|
513
532
|
methods.push(...cmpMeta.methods.filter(filterInternalProps).map(mapPropName));
|
|
514
533
|
}
|
|
534
|
+
const inlineComponentProps = outputTarget.inlineProperties ? internalProps : [];
|
|
515
535
|
/**
|
|
516
536
|
* For each component, we need to generate:
|
|
517
537
|
* 1. The @Component decorated class
|
|
518
538
|
* 2. Optionally the @NgModule decorated class (if includeSingleComponentAngularModules is true)
|
|
519
539
|
* 3. The component interface (using declaration merging for types).
|
|
520
540
|
*/
|
|
521
|
-
const componentDefinition = createAngularComponentDefinition(cmpMeta.tagName, inputs, outputs, methods, isCustomElementsBuild, isStandaloneBuild);
|
|
541
|
+
const componentDefinition = createAngularComponentDefinition(cmpMeta.tagName, inputs, outputs, methods, isCustomElementsBuild, isStandaloneBuild, inlineComponentProps);
|
|
522
542
|
const moduleDefinition = generateAngularModuleForComponent(cmpMeta.tagName);
|
|
523
543
|
const componentTypeDefinition = createComponentTypeDefinition(outputType, tagNameAsPascal, cmpMeta.events, componentCorePackage, customElementsDir);
|
|
524
544
|
proxyFileOutput.push(componentDefinition, '\n');
|
package/dist/output-angular.js
CHANGED
|
@@ -101,10 +101,11 @@ ${createImportStatement(componentLibImports, './angular-component-lib/utils')}\n
|
|
|
101
101
|
const { componentCorePackage, customElementsDir } = outputTarget;
|
|
102
102
|
for (let cmpMeta of components) {
|
|
103
103
|
const tagNameAsPascal = dashToPascalCase(cmpMeta.tagName);
|
|
104
|
-
const
|
|
104
|
+
const internalProps = [];
|
|
105
105
|
if (cmpMeta.properties) {
|
|
106
|
-
|
|
106
|
+
internalProps.push(...cmpMeta.properties.filter(filterInternalProps));
|
|
107
107
|
}
|
|
108
|
+
const inputs = internalProps.map(mapPropName);
|
|
108
109
|
if (cmpMeta.virtualProperties) {
|
|
109
110
|
inputs.push(...cmpMeta.virtualProperties.map(mapPropName));
|
|
110
111
|
}
|
|
@@ -117,13 +118,14 @@ ${createImportStatement(componentLibImports, './angular-component-lib/utils')}\n
|
|
|
117
118
|
if (cmpMeta.methods) {
|
|
118
119
|
methods.push(...cmpMeta.methods.filter(filterInternalProps).map(mapPropName));
|
|
119
120
|
}
|
|
121
|
+
const inlineComponentProps = outputTarget.inlineProperties ? internalProps : [];
|
|
120
122
|
/**
|
|
121
123
|
* For each component, we need to generate:
|
|
122
124
|
* 1. The @Component decorated class
|
|
123
125
|
* 2. Optionally the @NgModule decorated class (if includeSingleComponentAngularModules is true)
|
|
124
126
|
* 3. The component interface (using declaration merging for types).
|
|
125
127
|
*/
|
|
126
|
-
const componentDefinition = createAngularComponentDefinition(cmpMeta.tagName, inputs, outputs, methods, isCustomElementsBuild, isStandaloneBuild);
|
|
128
|
+
const componentDefinition = createAngularComponentDefinition(cmpMeta.tagName, inputs, outputs, methods, isCustomElementsBuild, isStandaloneBuild, inlineComponentProps);
|
|
127
129
|
const moduleDefinition = generateAngularModuleForComponent(cmpMeta.tagName);
|
|
128
130
|
const componentTypeDefinition = createComponentTypeDefinition(outputType, tagNameAsPascal, cmpMeta.events, componentCorePackage, customElementsDir);
|
|
129
131
|
proxyFileOutput.push(componentDefinition, '\n');
|
package/dist/types.d.ts
CHANGED
|
@@ -27,6 +27,12 @@ export interface OutputTargetAngular {
|
|
|
27
27
|
* - `standalone` - Generate a component with the `standalone` flag set to `true`.
|
|
28
28
|
*/
|
|
29
29
|
outputType?: OutputType;
|
|
30
|
+
/**
|
|
31
|
+
* Experimental (!)
|
|
32
|
+
* When true, tries to inline the properties of components. This is required to enable Angular Language Service
|
|
33
|
+
* to type-check and show jsdocs when using the components in html-templates.
|
|
34
|
+
*/
|
|
35
|
+
inlineProperties?: boolean;
|
|
30
36
|
}
|
|
31
37
|
export type ValueAccessorTypes = 'text' | 'radio' | 'select' | 'number' | 'boolean';
|
|
32
38
|
export interface ValueAccessorConfig {
|
package/package.json
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stencil/angular-output-target",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.1",
|
|
4
4
|
"description": "Angular output target for @stencil/core components.",
|
|
5
5
|
"main": "dist/index.cjs.js",
|
|
6
6
|
"module": "dist/index.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"require": "./dist/index.cjs.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
7
14
|
"types": "dist/index.d.ts",
|
|
8
15
|
"files": [
|
|
9
16
|
"dist/",
|
|
@@ -27,8 +34,8 @@
|
|
|
27
34
|
"prettier.base": "prettier \"./({angular-component-lib,src,test,__tests__}/**/*.{ts,tsx,js,jsx})|*.{ts,tsx,js,jsx}\"",
|
|
28
35
|
"prettier.dry-run": "pnpm run prettier.base --list-different",
|
|
29
36
|
"release": "np",
|
|
30
|
-
"test": "
|
|
31
|
-
"test.watch": "
|
|
37
|
+
"test": "vitest --run",
|
|
38
|
+
"test.watch": "vitest"
|
|
32
39
|
},
|
|
33
40
|
"repository": {
|
|
34
41
|
"type": "git",
|
|
@@ -44,29 +51,14 @@
|
|
|
44
51
|
"@angular/core": "8.2.14",
|
|
45
52
|
"@angular/forms": "8.2.14",
|
|
46
53
|
"@types/node": "^18.0.0",
|
|
47
|
-
"jest": "^27.0.0",
|
|
48
|
-
"jest-environment-jsdom": "^27.0.0",
|
|
49
54
|
"npm-run-all2": "^6.2.4",
|
|
50
55
|
"rimraf": "^5.0.0",
|
|
51
56
|
"rollup": "^2.23.1",
|
|
52
|
-
"typescript": "~5.0
|
|
57
|
+
"typescript": "~5.7.0",
|
|
58
|
+
"vitest": "^2.1.4"
|
|
53
59
|
},
|
|
54
60
|
"peerDependencies": {
|
|
55
61
|
"@stencil/core": ">=2.0.0 || >=3 || >= 4.0.0-beta.0 || >= 4.0.0"
|
|
56
62
|
},
|
|
57
|
-
"jest": {
|
|
58
|
-
"transform": {
|
|
59
|
-
"^.+\\.(js|ts|tsx)$": "<rootDir>/test/jest.preprocessor.js"
|
|
60
|
-
},
|
|
61
|
-
"testRegex": "(\\.(test|spec))\\.(ts?|tsx?|jsx?)$",
|
|
62
|
-
"moduleFileExtensions": [
|
|
63
|
-
"ts",
|
|
64
|
-
"tsx",
|
|
65
|
-
"js",
|
|
66
|
-
"json",
|
|
67
|
-
"jsx"
|
|
68
|
-
],
|
|
69
|
-
"testURL": "http://localhost"
|
|
70
|
-
},
|
|
71
63
|
"gitHead": "a3588e905186a0e86e7f88418fd5b2f9531b55e0"
|
|
72
64
|
}
|
|
@@ -21,7 +21,7 @@ export class BooleanValueAccessor extends ValueAccessor {
|
|
|
21
21
|
constructor(el: ElementRef) {
|
|
22
22
|
super(el);
|
|
23
23
|
}
|
|
24
|
-
writeValue(value: any) {
|
|
24
|
+
override writeValue(value: any) {
|
|
25
25
|
this.el.nativeElement.checked = this.lastValue = value == null ? false : value;
|
|
26
26
|
}
|
|
27
27
|
}
|
|
@@ -21,7 +21,7 @@ export class NumericValueAccessor extends ValueAccessor {
|
|
|
21
21
|
constructor(el: ElementRef) {
|
|
22
22
|
super(el);
|
|
23
23
|
}
|
|
24
|
-
registerOnChange(fn: (_: number | null) => void) {
|
|
24
|
+
override registerOnChange(fn: (_: number | null) => void) {
|
|
25
25
|
super.registerOnChange(value => {
|
|
26
26
|
fn(value === '' ? null : parseFloat(value));
|
|
27
27
|
});
|