@ui5/webcomponents-tools 2.15.0 → 2.15.2

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.
Files changed (57) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/lib/cem/cem.js +1 -1
  3. package/lib/cem/patch/@custom-elements-manifest/analyzer/cli.js +128 -0
  4. package/lib/cem/patch/@custom-elements-manifest/analyzer/package.json +59 -0
  5. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/browser-entrypoint.js +23 -0
  6. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/create.js +117 -0
  7. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/analyse-phase/arrow-function.js +26 -0
  8. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/analyse-phase/class-jsdoc.js +157 -0
  9. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/analyse-phase/classes.js +20 -0
  10. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/analyse-phase/creators/createArrowFunction.js +17 -0
  11. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/analyse-phase/creators/createAttribute.js +24 -0
  12. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/analyse-phase/creators/createClass.js +301 -0
  13. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/analyse-phase/creators/createClassField.js +26 -0
  14. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/analyse-phase/creators/createFunctionLike.js +73 -0
  15. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/analyse-phase/creators/createMixin.js +33 -0
  16. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/analyse-phase/creators/createVariable.js +22 -0
  17. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/analyse-phase/creators/handlers.js +338 -0
  18. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/analyse-phase/custom-elements-define-calls.js +90 -0
  19. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/analyse-phase/exports.js +156 -0
  20. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/analyse-phase/function-like.js +24 -0
  21. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/analyse-phase/mixins.js +29 -0
  22. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/analyse-phase/reexported-wrapped-mixin-exports.js +84 -0
  23. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/analyse-phase/variables.js +34 -0
  24. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/collect-phase/collect-imports.js +101 -0
  25. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/framework-plugins/catalyst/catalyst.js +11 -0
  26. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/framework-plugins/catalyst/controller.js +34 -0
  27. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/framework-plugins/catalyst-major-2/catalyst.js +11 -0
  28. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/framework-plugins/catalyst-major-2/controller.js +34 -0
  29. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/framework-plugins/decorators/attr.js +53 -0
  30. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/framework-plugins/decorators/custom-element-decorator.js +36 -0
  31. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/framework-plugins/fast/fast.js +7 -0
  32. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/framework-plugins/lit/lit.js +13 -0
  33. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/framework-plugins/lit/member-denylist.js +21 -0
  34. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/framework-plugins/lit/method-denylist.js +20 -0
  35. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/framework-plugins/lit/property-decorator.js +94 -0
  36. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/framework-plugins/lit/static-properties.js +121 -0
  37. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/framework-plugins/lit/utils.js +66 -0
  38. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/framework-plugins/stencil/stencil.js +129 -0
  39. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/index.js +80 -0
  40. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/link-phase/cleanup-classes.js +25 -0
  41. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/link-phase/field-denylist.js +22 -0
  42. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/link-phase/method-denylist.js +25 -0
  43. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/post-processing/apply-inheritance.js +78 -0
  44. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/post-processing/is-custom-element.js +34 -0
  45. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/post-processing/link-class-to-tagname.js +27 -0
  46. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/post-processing/remove-unexported-declarations.js +23 -0
  47. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/post-processing/resolve-initializers.js +52 -0
  48. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/utils/ast-helpers.js +186 -0
  49. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/utils/cli-helpers.js +164 -0
  50. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/utils/exports.js +44 -0
  51. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/utils/find-external-manifests.js +67 -0
  52. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/utils/imports.js +25 -0
  53. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/utils/index.js +71 -0
  54. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/utils/jsdoc.js +19 -0
  55. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/utils/manifest-helpers.js +194 -0
  56. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/utils/mixins.js +112 -0
  57. package/package.json +3 -3
@@ -0,0 +1,52 @@
1
+ import { url } from "../../utils/index.js";
2
+
3
+ export function resolveInitializersPlugin() {
4
+ return {
5
+ name: 'CORE - RESOLVE-INITIALIZERS',
6
+ /**
7
+ * If a class field has a `resolveInitializer` property on it, it means its being assigned a variable. We need to resolve that variable, and get its type and default value
8
+ */
9
+ packageLinkPhase({customElementsManifest}) {
10
+ customElementsManifest?.modules?.forEach(mod => {
11
+ mod?.declarations?.forEach(declaration => {
12
+ declaration?.members
13
+ ?.filter(({resolveInitializer}) => resolveInitializer)
14
+ ?.forEach(member => {
15
+ /** We ignore variables imported from a third party package */
16
+ if('package' in member.resolveInitializer) {
17
+ delete member.resolveInitializer;
18
+ return;
19
+ };
20
+
21
+ /** Find the module */
22
+ const foundModule = customElementsManifest?.modules?.find(({path}) => {
23
+ let toResolve = url(member?.resolveInitializer?.module);
24
+ const modulePath = url(path);
25
+
26
+ if(!toResolve.endsWith('.js') && !toResolve.endsWith('.ts')) {
27
+ toResolve += '.ts';
28
+ }
29
+
30
+ return toResolve === modulePath;
31
+ });
32
+
33
+ if(foundModule) {
34
+ /** Find the declaration */
35
+ const foundReference = foundModule?.declarations?.find(declaration => declaration?.name === member?.default);
36
+ /** Overwrite the type with the type of the reference we found */
37
+ if(foundReference?.type && !member?.type) {
38
+ member.type = foundReference.type;
39
+ }
40
+
41
+ if(foundReference?.default) {
42
+ member.default = foundReference?.default;
43
+ }
44
+ }
45
+
46
+ delete member.resolveInitializer;
47
+ });
48
+ });
49
+ });
50
+ }
51
+ }
52
+ }
@@ -0,0 +1,186 @@
1
+ import ts from 'typescript';
2
+ import { safe } from './index.js';
3
+
4
+ /**
5
+ * AST HELPERS
6
+ */
7
+
8
+ export const isProperty = node => ts.isPropertyDeclaration(node) || ts.isGetAccessor(node) || ts.isSetAccessor(node);
9
+
10
+ /**
11
+ * @example this.dispatchEvent(new Event('foo'));
12
+ */
13
+ export const isDispatchEvent = node => node.expression?.name?.getText() === 'dispatchEvent' && node?.expression?.expression?.kind === ts.SyntaxKind.ThisKeyword
14
+
15
+ export const isReturnStatement = statement => statement?.kind === ts.SyntaxKind.ReturnStatement;
16
+
17
+ /**
18
+ * @example customElements.define('my-el', MyEl);
19
+ * @example window.customElements.define('my-el', MyEl);
20
+ */
21
+ export const isCustomElementsDefineCall = node => (node?.expression?.getText() === 'customElements' || node?.expression?.getText() === 'window.customElements' || node?.expression?.getText() === 'globalThis.customElements') && node?.name?.getText() === 'define' && node?.parent?.kind === ts.SyntaxKind.CallExpression;
22
+
23
+ /**
24
+ * @example @attr
25
+ * @example @attribute
26
+ */
27
+ export function hasAttrAnnotation(member) {
28
+ return member?.jsDoc?.some(jsDoc => jsDoc?.tags?.some(tag => safe(() => ["attribute", "attr"].includes(tag?.tagName?.getText()))));
29
+ }
30
+
31
+
32
+ /**
33
+ * Whether or not node is:
34
+ * - Number
35
+ * - String
36
+ * - Boolean
37
+ * - Null
38
+ */
39
+ export function isPrimitive(node) {
40
+ return node && (ts.isNumericLiteral(node) ||
41
+ ts.isStringLiteral(node) ||
42
+ node?.kind === ts.SyntaxKind.NullKeyword ||
43
+ node?.kind === ts.SyntaxKind.TrueKeyword ||
44
+ node?.kind === ts.SyntaxKind.FalseKeyword) ||
45
+ // Handle only empty arrays for now
46
+ (node?.kind === ts.SyntaxKind.ArrayLiteralExpression && node?.elements?.length === 0)
47
+ }
48
+
49
+ /**
50
+ * Checks if a VariableStatement has an initializer
51
+ * @example `let foo;` will return false
52
+ * @example `let foo = '';` will return true
53
+ */
54
+ export function hasInitializer(node) {
55
+ return node?.declarationList?.declarations?.some(declaration => declaration?.initializer);
56
+ }
57
+
58
+ export function getElementNameFromDecorator(decorator) {
59
+ const argument = decorator.expression.arguments[0];
60
+
61
+ /**
62
+ * @example @customElement('my-el')
63
+ */
64
+ if(argument.kind === ts.SyntaxKind.StringLiteral) {
65
+ return argument.text;
66
+ }
67
+
68
+ /**
69
+ * @example @customElement({
70
+ * name: 'my-el',
71
+ * template
72
+ * })
73
+ */
74
+ if(argument.kind === ts.SyntaxKind.ObjectLiteralExpression) {
75
+ let result;
76
+ argument?.properties?.forEach(property => {
77
+ if(property?.name?.getText() === 'name') {
78
+ result = property?.initializer?.text;
79
+ }
80
+ });
81
+ return result;
82
+ }
83
+ }
84
+
85
+
86
+ /**
87
+ * Gets the name of an attr from a decorators callExpression
88
+ * @example @attr({attribute: 'my-el'})
89
+ */
90
+ export const getOptionsObject = decorator => decorator?.expression?.arguments?.find(arg => arg.kind === ts.SyntaxKind.ObjectLiteralExpression);
91
+
92
+ /**
93
+ * Get the return value expression of a return statement, omitting the type assertion
94
+ */
95
+ export const getReturnValue = returnStatement => {
96
+ let value = returnStatement.expression?.kind === ts.SyntaxKind.AsExpression
97
+ ? returnStatement.expression.expression.getText()
98
+ : returnStatement.expression?.getText()
99
+
100
+ return value?.split?.(' ')?.[0];
101
+ }
102
+
103
+ /**
104
+ * Is this class member a static member?
105
+ */
106
+ export const isStaticMember = member =>
107
+ member?.modifiers?.some?.(x => x.kind === ts.SyntaxKind.StaticKeyword);
108
+
109
+ /**
110
+ * @param {import('typescript').Expression} initializer
111
+ * @return {initializer is import('typescript').AsExpression & { type: import("typescript").TypeReference }}
112
+ */
113
+ function isAsConst(initializer) {
114
+ return (
115
+ initializer &&
116
+ initializer.kind &&
117
+ ts.isAsExpression(initializer) &&
118
+ ts.isTypeReferenceNode(initializer.type) &&
119
+ initializer.type.typeName.getText() === 'const'
120
+ );
121
+ }
122
+
123
+
124
+ /**
125
+ * Does the name have an initializer with `as const`?
126
+ * @param {import('typescript').Node} node
127
+ * @return {Boolean}
128
+ */
129
+ export function isWellKnownType(node) {
130
+ return (
131
+ node?.initializer && (
132
+ isAsConst(node?.initializer)
133
+ )
134
+ );
135
+ }
136
+
137
+ /**
138
+ * Whether or not a node has an `@ignore` jsdoc annotation
139
+ */
140
+ export const hasIgnoreJSDoc = node => node?.jsDoc?.some(doc => doc?.tags?.some(tag => safe(() => tag?.tagName?.getText()) === 'ignore' || safe(() => tag?.tagName?.getText()) === 'internal'));
141
+
142
+
143
+ /**
144
+ * @example this.__onClick = this.__onClick.bind(this);
145
+ */
146
+ export function isBindCall(statement) {
147
+ const { expression } = statement;
148
+ if(expression) {
149
+ const leftName = expression?.left?.name?.getText();
150
+ const rightName = expression?.right?.expression?.expression?.name?.getText();
151
+ const isBind = expression?.right?.expression?.name?.getText() === 'bind';
152
+
153
+ if(leftName === undefined || rightName === undefined) return false;
154
+
155
+ if(leftName === rightName && isBind) {
156
+ return true;
157
+ }
158
+ }
159
+ return false;
160
+ }
161
+
162
+ /**
163
+ * Does the variable have an `@ignore` or `@internal` JSDoc tag?
164
+ * @param {import('typescript').Node|string} nodeOrName
165
+ * @param {import('typescript').SourceFile} sourceFile
166
+ * @return {import('typescript').Node}
167
+ */
168
+ export function getDeclarationInFile(nodeOrName, sourceFile) {
169
+ let name = nodeOrName;
170
+ if (typeof nodeOrName === 'string') {
171
+ if (!sourceFile)
172
+ throw new Error('must provide sourceFile when first argument is a string');
173
+ } else {
174
+ sourceFile = nodeOrName?.getSourceFile();
175
+ name = nodeOrName?.name?.getText();
176
+ }
177
+ if (!name)
178
+ return undefined;
179
+ const sourceFileStatements = sourceFile.statements ?? [];
180
+ return sourceFileStatements.find(statement => {
181
+ if (ts.isVariableStatement(statement))
182
+ return statement.declarationList.declarations.find(declaration => declaration.name.getText() === name)
183
+ else if (statement.name?.getText)
184
+ return statement.name.getText() === name;
185
+ });
186
+ }
@@ -0,0 +1,164 @@
1
+ import { readConfig, ConfigLoaderError } from '@web/config-loader';
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+ import commandLineArgs from 'command-line-args';
5
+ import { has } from './index.js';
6
+ import { createRequire } from 'module';
7
+
8
+ const require = createRequire(import.meta.url);
9
+ const { version } = require('../../package.json');
10
+
11
+ const IGNORE = [
12
+ '!node_modules/**/*.*',
13
+ '!bower_components/**/*.*',
14
+ '!**/*.test.{js,ts}',
15
+ '!**/*.suite.{js,ts}',
16
+ '!**/*.config.{js,ts}',
17
+ '!**/*.d.ts',
18
+ ];
19
+
20
+ export function mergeGlobsAndExcludes(defaults, userConfig, cliConfig) {
21
+ const hasProvidedCliGlobs = has(cliConfig?.globs) || has(userConfig?.globs);
22
+
23
+ if(hasProvidedCliGlobs) {
24
+ defaults.globs = defaults.globs.filter(glob => glob !== '**/*.{js,ts,tsx}');
25
+ }
26
+
27
+ const merged = [
28
+ ...defaults.globs,
29
+ ...(userConfig?.globs || []),
30
+ ...(cliConfig?.globs || []),
31
+ ...(userConfig?.exclude?.map((i) => `!${i}`) || []),
32
+ ...(cliConfig?.exclude?.map((i) => `!${i}`) || []),
33
+ ...IGNORE,
34
+ ];
35
+
36
+ return merged;
37
+ }
38
+
39
+ export async function getUserConfig(configPath, cwd) {
40
+ let userConfig = {};
41
+ try {
42
+ userConfig = await readConfig('custom-elements-manifest.config', configPath, cwd);
43
+ } catch (error) {
44
+ if (error instanceof ConfigLoaderError) {
45
+ console.error(error.message);
46
+ return;
47
+ }
48
+ console.error(error);
49
+ return;
50
+ }
51
+ return userConfig || {};
52
+ }
53
+
54
+ export const DEFAULTS = {
55
+ outdir: '',
56
+ globs: ['**/*.{js,ts,tsx}'],
57
+ dev: false,
58
+ quiet: false,
59
+ packagejson: true,
60
+ watch: false,
61
+ litelement: false,
62
+ stencil: false,
63
+ fast: false,
64
+ catalyst: false,
65
+ 'catalyst-major-2': false,
66
+ }
67
+
68
+ export function getCliConfig(argv) {
69
+ const optionDefinitions = [
70
+ { name: 'config', type: String},
71
+ { name: 'globs', type: String, multiple: true },
72
+ { name: 'exclude', type: String, multiple: true },
73
+ { name: 'outdir', type: String },
74
+ { name: 'dev', type: Boolean },
75
+ { name: 'quiet', type: Boolean },
76
+ { name: 'dependencies', type: Boolean },
77
+ { name: 'packagejson', type: Boolean },
78
+ { name: 'watch', type: Boolean },
79
+ { name: 'litelement', type: Boolean },
80
+ { name: 'stencil', type: Boolean },
81
+ { name: 'fast', type: Boolean },
82
+ { name: 'catalyst', type: Boolean },
83
+ { name: 'catalyst-major-2', type: Boolean },
84
+ ];
85
+
86
+ return commandLineArgs(optionDefinitions, { argv });
87
+ }
88
+
89
+ export async function addFrameworkPlugins(mergedOptions) {
90
+ let plugins = [];
91
+ if(mergedOptions?.litelement) {
92
+ const { litPlugin } = await import('../features/framework-plugins/lit/lit.js');
93
+ plugins = [...(litPlugin() || [])]
94
+ }
95
+
96
+ if(mergedOptions?.fast) {
97
+ const { fastPlugin } = await import('../features/framework-plugins/fast/fast.js');
98
+ plugins = [...(fastPlugin() || [])]
99
+ }
100
+
101
+ if(mergedOptions?.stencil) {
102
+ const { stencilPlugin } = await import('../features/framework-plugins/stencil/stencil.js');
103
+ plugins.push(stencilPlugin());
104
+ }
105
+
106
+ if(mergedOptions?.catalyst) {
107
+ const { catalystPlugin } = await import('../features/framework-plugins/catalyst/catalyst.js');
108
+ plugins = [...(catalystPlugin() || [])]
109
+ }
110
+
111
+ if(mergedOptions?.['catalyst-major-2']) {
112
+ const { catalystPlugin2 } = await import('../features/framework-plugins/catalyst-major-2/catalyst.js');
113
+ plugins = [...(catalystPlugin2() || [])]
114
+ }
115
+
116
+ return plugins;
117
+ }
118
+
119
+ export function timestamp() {
120
+ const date = new Date();
121
+ return date.toLocaleTimeString();
122
+ }
123
+
124
+ export function addCustomElementsPropertyToPackageJson(outdir) {
125
+ const packageJsonPath = `${process.cwd()}${path.sep}package.json`;
126
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath).toString());
127
+ const manifestPath = path.posix.join(outdir, 'custom-elements.json');
128
+ if(packageJson?.customElements) {
129
+ if(packageJson?.customElements !== manifestPath) {
130
+ packageJson.customElements = manifestPath;
131
+ fs.writeFileSync(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\n`);
132
+ }
133
+ return;
134
+ } else {
135
+ packageJson.customElements = manifestPath;
136
+ fs.writeFileSync(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\n`);
137
+ }
138
+ }
139
+
140
+ export const MENU = `
141
+ @custom-elements-manifest/analyzer (${version})
142
+
143
+ Available commands:
144
+ | Command/option | Type | Description | Example |
145
+ | ------------------ | ---------- | ----------------------------------------------------------- | ------------------------------------------------------- |
146
+ | analyze | | Analyze your components | |
147
+ | --config | string | Path to custom config location | \`--config "../custom-elements-manifest.config.js"\` |
148
+ | --globs | string[] | Globs to analyze | \`--globs "foo.js"\` |
149
+ | --exclude | string[] | Globs to exclude | \`--exclude "foo.js"\` |
150
+ | --outdir | string | Directory to output the Manifest to | \`--outdir dist\` |
151
+ | --dependencies | boolean | Include third party custom elements manifests | \`--dependencies\` |
152
+ | --packagejson | boolean | Output CEM path to \`package.json\`, defaults to true | \`--packagejson\` |
153
+ | --watch | boolean | Enables watch mode, generates a new manifest on file change | \`--watch\` |
154
+ | --dev | boolean | Enables extra logging for debugging | \`--dev\` |
155
+ | --quiet | boolean | Hides all logging | \`--quiet\` |
156
+ | --litelement | boolean | Enable special handling for LitElement syntax | \`--litelement\` |
157
+ | --fast | boolean | Enable special handling for FASTElement syntax | \`--fast\` |
158
+ | --stencil | boolean | Enable special handling for Stencil syntax | \`--stencil\` |
159
+ | --catalyst | boolean | Enable special handling for Catalyst syntax | \`--catalyst\` |
160
+ | --catalyst-major-2 | boolean | Enable special handling for Catalyst syntax ^2.0.0 | \`--catalyst-major-2\` |
161
+
162
+ Examples:
163
+ custom-elements-manifest analyze --litelement --globs "**/*.js" --exclude "foo.js" "bar.js"
164
+ `
@@ -0,0 +1,44 @@
1
+ import ts from 'typescript';
2
+ import { has } from './index.js';
3
+
4
+ /**
5
+ * UTILITIES RELATED TO MODULE EXPORTS
6
+ */
7
+
8
+ export function hasExportModifier(node) {
9
+ if (has(node?.modifiers)) {
10
+ if (node.modifiers.some(mod => mod.kind === ts.SyntaxKind.ExportKeyword)) {
11
+ return true;
12
+ }
13
+ }
14
+ return false;
15
+ }
16
+
17
+ export function hasDefaultModifier(node) {
18
+ if (has(node?.modifiers)) {
19
+ if (node.modifiers.some(mod => mod.kind === ts.SyntaxKind.DefaultKeyword)) {
20
+ return true;
21
+ }
22
+ }
23
+ return false;
24
+ }
25
+
26
+ /**
27
+ * @example export { var1, var2 };
28
+ */
29
+ export function hasNamedExports(node) {
30
+ if (has(node?.exportClause?.elements)) {
31
+ return true;
32
+ }
33
+ return false;
34
+ }
35
+
36
+ /**
37
+ * @example export { var1, var2 } from 'foo';
38
+ */
39
+ export function isReexport(node) {
40
+ if (node?.moduleSpecifier !== undefined) {
41
+ return true;
42
+ }
43
+ return false;
44
+ }
@@ -0,0 +1,67 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { findDependencies, splitPath } from '@custom-elements-manifest/find-dependencies';
4
+
5
+ /**
6
+ * @typedef {import('custom-elements-manifest/schema').Package} Package
7
+ */
8
+
9
+ /**
10
+ * @param {string[]} paths
11
+ * @param {{
12
+ * nodeModulesDepth?: number,
13
+ * basePath?: string,
14
+ * }} [options]
15
+ */
16
+ export async function findExternalManifests(paths, {basePath = process.cwd(), nodeModulesDepth}) {
17
+ /** @type {Package[]} */
18
+ const cemsToMerge = [];
19
+ const visited = new Set();
20
+
21
+ const dependencies = await findDependencies(paths, {basePath, nodeModulesDepth});
22
+
23
+ dependencies?.forEach((dependencyPath) => {
24
+ const isCurrentPackageRegex = new RegExp(`^${basePath}\\${path.sep}(?!.*node_modules).*`);
25
+ if (isCurrentPackageRegex.test(dependencyPath)) {
26
+ // Make sure that we do not look for custom-elements.json in our own package
27
+ return;
28
+ }
29
+
30
+ const { packageRoot, packageName } = splitPath(dependencyPath);
31
+
32
+ if(visited.has(packageName)) return;
33
+ visited.add(packageName);
34
+
35
+ const packageJsonPath = `${packageRoot}${path.sep}package.json`;
36
+ const cemPath = `${packageRoot}${path.sep}custom-elements.json`;
37
+
38
+ /** Try to find `custom-elements.json` at `node_modules/specifier/custom-elements.json` */
39
+ if(fs.existsSync(cemPath)) {
40
+ try {
41
+ const cem = JSON.parse(fs.readFileSync(cemPath).toString());
42
+ cemsToMerge.push(cem);
43
+ return;
44
+ } catch(e) {
45
+ throw new Error(`Failed to read custom-elements.json at path "${cemPath}". \n\n${e.stack}`);
46
+ }
47
+ }
48
+
49
+ /** See if the `package.json` has a `customElements` field or if it has listed `./customElements` in its export map */
50
+ if(fs.existsSync(packageJsonPath)) {
51
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath).toString());
52
+ const cemLocation = packageJson?.customElements || packageJson?.exports?.['./customElements'];
53
+
54
+ if(cemLocation) {
55
+ try {
56
+ const cemPath = path.resolve(packageRoot, cemLocation);
57
+ const cem = JSON.parse(fs.readFileSync(cemPath).toString());
58
+ cemsToMerge.push(cem);
59
+ } catch(e) {
60
+ throw new Error(`Failed to read custom-elements.json at path "${cemPath}". \n\n${e.stack}`);
61
+ }
62
+ }
63
+ }
64
+ });
65
+
66
+ return cemsToMerge;
67
+ }
@@ -0,0 +1,25 @@
1
+ import { has } from './index.js';
2
+
3
+ /**
4
+ * UTILITIES RELATED TO MODULE IMPORTS
5
+ */
6
+
7
+ /** @example import defaultExport from 'foo'; */
8
+ export function hasDefaultImport(node) {
9
+ return !!node?.importClause?.name;
10
+ }
11
+
12
+ /** @example import {namedA, namedB} from 'foo'; */
13
+ export function hasNamedImport(node) {
14
+ return has(node?.importClause?.namedBindings?.elements);
15
+ }
16
+
17
+ /** @example import * as name from './my-module.js'; */
18
+ export function hasAggregatingImport(node) {
19
+ return !!node?.importClause?.namedBindings?.name && !hasNamedImport(node);
20
+ }
21
+
22
+ /** @example import './my-module.js'; */
23
+ export function hasSideEffectImport(node) {
24
+ return 'importClause' in node && node.importClause == null;
25
+ }
@@ -0,0 +1,71 @@
1
+ import ts from 'typescript';
2
+
3
+ /**
4
+ * GENERAL UTILITIES
5
+ */
6
+
7
+ export const has = arr => Array.isArray(arr) && arr.length > 0;
8
+
9
+ /**
10
+ * @example node?.modifiers?.find(decorator('Component'))
11
+ */
12
+ export const decorator = type => decorator => ts.isDecorator(decorator) && decorator?.expression?.expression?.getText() === type || decorator?.expression?.getText() === type;
13
+
14
+ export function isBareModuleSpecifier(specifier) {
15
+ return !!specifier?.replace(/'/g, '')[0].match(/[@a-zA-Z]/g);
16
+ }
17
+
18
+ export const url = path => new URL('', `file:///${path}`)?.pathname;
19
+
20
+ export function resolveModuleOrPackageSpecifier(moduleDoc, context, name) {
21
+ const foundImport = context?.imports?.find(_import => _import.name === name);
22
+
23
+ /* item is imported from another file */
24
+ if(foundImport) {
25
+ if(foundImport.isBareModuleSpecifier) {
26
+ /* import is from 3rd party package */
27
+ return { package: foundImport.importPath }
28
+ } else {
29
+ /* import is imported from a local module */
30
+ return { module: new URL(foundImport.importPath, `file:///${moduleDoc.path}`).pathname }
31
+ }
32
+ } else {
33
+ /* item is in current module */
34
+ return { module: moduleDoc.path }
35
+ }
36
+ }
37
+
38
+ export const toKebabCase = str => {
39
+ return str.split('').map((letter, idx) => {
40
+ return letter.toUpperCase() === letter
41
+ ? `${idx !== 0 ? '-' : ''}${letter.toLowerCase()}`
42
+ : letter;
43
+ }).join('');
44
+ }
45
+
46
+ /**
47
+ * TS seems to struggle sometimes with the `.getText()` method on JSDoc annotations, like `@deprecated` in ts v4.0.0 and `@override` in ts v4.3.2
48
+ * This is a bug in TS, but still annoying, so we add some safety rails here
49
+ */
50
+ export const safe = (cb, returnType = '') => {
51
+ try {
52
+ return cb();
53
+ } catch {
54
+ return returnType;
55
+ }
56
+ }
57
+
58
+ export function withErrorHandling(name, cb) {
59
+ try {
60
+ cb()
61
+ } catch(e) {
62
+ let errorMessage = '';
63
+ const externalError = `Looks like you've hit an error in third party plugin: ${name}. Please try to create a minimal reproduction and inform the author of the ${name} plugin.`;
64
+ const coreError = `Looks like you've hit an error in the core library. Please try to create a minimal reproduction at https://custom-elements-manifest.netlify.com and create an issue at: https://github.com/open-wc/custom-elements-manifest/issues`;
65
+ if(name) {
66
+ errorMessage = name.startsWith('CORE') ? coreError : externalError;
67
+ }
68
+
69
+ throw new Error(`\n\n[${name ?? 'unnamed-plugin'}]: ${errorMessage}\n\n ${e.stack}\n`);
70
+ }
71
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * UTILITIES RELATED TO JSDOC
3
+ */
4
+
5
+ export function handleJsDocType(type) {
6
+ return type?.replace(/(import\(.+?\).)/g, '') || '';
7
+ }
8
+
9
+ export function normalizeDescription(desc) {
10
+ if (Array.isArray(desc)) {
11
+ desc = desc.reduce((prev, curr) => prev += curr.getText(), '');
12
+ }
13
+
14
+ if (typeof desc === 'string' && desc?.startsWith('- ')) {
15
+ desc = desc.slice(2);
16
+ }
17
+
18
+ return desc;
19
+ }