@stencil/core 5.0.0-beta.2 → 5.0.0-beta.21
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/app-data/index.d.ts +1 -1
- package/dist/{client-BRu0GtRn.mjs → client-CVXS3JZ-.mjs} +105 -60
- package/dist/compiler/browser.d.ts +110 -79
- package/dist/compiler/browser.js +571 -269
- package/dist/compiler/index.d.mts +110 -2
- package/dist/compiler/index.mjs +3 -3
- package/dist/compiler/utils/index.d.mts +1 -1
- package/dist/{compiler-C8yf59oh.mjs → compiler-ApS_Tn_4.mjs} +4460 -2742
- package/dist/declarations/stencil-public-compiler.d.ts +144 -120
- package/dist/declarations/stencil-public-docs.d.ts +23 -0
- package/dist/declarations/stencil-public-runtime.d.ts +60 -58
- package/dist/{index-BopBfjPu.d.mts → index-B9-Byy8z.d.mts} +142 -118
- package/dist/{index-BXAcVN2j.d.ts → index-BHciXst1.d.ts} +57 -55
- package/dist/{index-DmHmu3y0.d.mts → index-Dz1G9pWZ.d.mts} +2 -2
- package/dist/index.d.mts +3 -0
- package/dist/index.mjs +1 -1
- package/dist/jsx-runtime.mjs +1 -1
- package/dist/{node-75gQKkFz.mjs → node-Bz2SOK7_.mjs} +6 -35
- package/dist/runtime/client/lazy.js +228 -180
- package/dist/runtime/client/runtime.d.ts +67 -66
- package/dist/runtime/client/runtime.js +228 -180
- package/dist/runtime/index.d.ts +4 -5
- package/dist/runtime/index.js +226 -178
- package/dist/runtime/server/index.d.mts +11 -11
- package/dist/runtime/server/index.mjs +287 -220
- package/dist/runtime/server/runner.d.mts +4 -4
- package/dist/runtime/server/runner.mjs +82 -54
- package/dist/signals/index.d.ts +11 -2
- package/dist/signals/index.js +11 -1
- package/dist/sys/node/index.d.mts +1 -1
- package/dist/sys/node/index.mjs +1 -1
- package/dist/sys/node/worker.mjs +2 -2
- package/dist/testing/index.d.mts +129 -112
- package/dist/testing/index.mjs +244 -193
- package/package.json +8 -4
package/dist/compiler/browser.js
CHANGED
|
@@ -1501,7 +1501,7 @@ const scopeSelector = (selector, scopeSelectorText, hostSelector, slotSelector)
|
|
|
1501
1501
|
else return shallowPart.trim();
|
|
1502
1502
|
}).join(", ");
|
|
1503
1503
|
};
|
|
1504
|
-
const isScopableAtRule = (selector) => selector.startsWith("@media") || selector.startsWith("@supports") || selector.startsWith("@page") || selector.startsWith("@document") || selector.startsWith("@layer");
|
|
1504
|
+
const isScopableAtRule = (selector) => selector.startsWith("@media") || selector.startsWith("@supports") || selector.startsWith("@page") || selector.startsWith("@document") || selector.startsWith("@layer") || selector.startsWith("@container");
|
|
1505
1505
|
const scopeSelectors = (cssText, scopeSelectorText, hostSelector, slotSelector, commentOriginalSelector) => {
|
|
1506
1506
|
return processRules(cssText, (rule) => {
|
|
1507
1507
|
let selector = rule.selector;
|
|
@@ -1693,6 +1693,18 @@ const createJsVarName = (fileName) => {
|
|
|
1693
1693
|
}
|
|
1694
1694
|
return fileName;
|
|
1695
1695
|
};
|
|
1696
|
+
/**
|
|
1697
|
+
* Create a function that lowercases the first string parameter before passing it to the provided function
|
|
1698
|
+
* @param fn the function to pass the lowercased path to
|
|
1699
|
+
* @returns the result of the provided function
|
|
1700
|
+
*/
|
|
1701
|
+
const lowerPathParam = (fn) => (p) => fn(p.toLowerCase());
|
|
1702
|
+
/**
|
|
1703
|
+
* Determine if a stringified file path is a TypeScript declaration file based on the extension at the end of the path.
|
|
1704
|
+
* @param p the path to evaluate
|
|
1705
|
+
* @returns `true` if the path ends in `.d.ts` (case-sensitive), `false` otherwise.
|
|
1706
|
+
*/
|
|
1707
|
+
const isDtsFile = lowerPathParam((p) => p.endsWith(".d.ts") || p.endsWith(".d.mts") || p.endsWith(".d.cts"));
|
|
1696
1708
|
function getTextDocs(docs) {
|
|
1697
1709
|
if (docs == null) return "";
|
|
1698
1710
|
return [escapeWithPattern(docs.text.replace(LINE_BREAK_REGEX, " "), /\*\//, "*\\/", true), ...docs.tags.filter((tag) => tag.name !== "internal").map((tag) => {
|
|
@@ -1832,7 +1844,7 @@ const validateComponentTag = (tag) => {
|
|
|
1832
1844
|
};
|
|
1833
1845
|
//#endregion
|
|
1834
1846
|
//#region src/version.ts
|
|
1835
|
-
const version = "5.0.0-beta.
|
|
1847
|
+
const version = "5.0.0-beta.21";
|
|
1836
1848
|
//#endregion
|
|
1837
1849
|
//#region src/compiler/events.ts
|
|
1838
1850
|
const buildEvents = () => {
|
|
@@ -1895,6 +1907,7 @@ const resolve = (id, _opts, cb) => {
|
|
|
1895
1907
|
};
|
|
1896
1908
|
//#endregion
|
|
1897
1909
|
//#region src/compiler/sys/resolve/resolve-utils.ts
|
|
1910
|
+
const isLocalModule = (p) => p.startsWith(".") || p.startsWith("/");
|
|
1898
1911
|
const isNodeModulePath = (p) => normalizePath(p).split("/").includes("node_modules");
|
|
1899
1912
|
const getModuleId = (orgImport) => {
|
|
1900
1913
|
if (orgImport.startsWith("~")) orgImport = orgImport.substring(1);
|
|
@@ -2345,15 +2358,6 @@ const createSystem = (c) => {
|
|
|
2345
2358
|
}
|
|
2346
2359
|
return results;
|
|
2347
2360
|
};
|
|
2348
|
-
/**
|
|
2349
|
-
* `self` is the global namespace object used within a web worker.
|
|
2350
|
-
* `window` is the browser's global namespace object (I reorganized this to check the reference on that second)
|
|
2351
|
-
* `global` is Node's global namespace object. https://nodejs.org/api/globals.html#globals_global
|
|
2352
|
-
*
|
|
2353
|
-
* loading in this order should allow workers, which are most common, then browser,
|
|
2354
|
-
* then Node to grab the reference to fetch correctly.
|
|
2355
|
-
*/
|
|
2356
|
-
const fetch = typeof self !== "undefined" ? self?.fetch : typeof window !== "undefined" ? window?.fetch : typeof global !== "undefined" ? global?.fetch : void 0;
|
|
2357
2361
|
const writeFile = async (p, data) => writeFileSync(p, data);
|
|
2358
2362
|
const tmpDirSync = () => "/.tmp";
|
|
2359
2363
|
const tick = Promise.resolve();
|
|
@@ -2364,6 +2368,7 @@ const createSystem = (c) => {
|
|
|
2364
2368
|
if (typeof hashLength === "number") hashHex = hashHex.slice(0, hashLength);
|
|
2365
2369
|
return hashHex;
|
|
2366
2370
|
};
|
|
2371
|
+
const generateFileHash = async (filePath, hashLength) => generateContentHash(readFileSync(filePath), hashLength);
|
|
2367
2372
|
const copy = async (copyTasks, srcDir) => {
|
|
2368
2373
|
const results = {
|
|
2369
2374
|
diagnostics: [],
|
|
@@ -2373,9 +2378,6 @@ const createSystem = (c) => {
|
|
|
2373
2378
|
logger.info("todo, copy task", copyTasks.length, srcDir);
|
|
2374
2379
|
return results;
|
|
2375
2380
|
};
|
|
2376
|
-
const getEnvironmentVar = (key) => {
|
|
2377
|
-
return process$1?.env[key];
|
|
2378
|
-
};
|
|
2379
2381
|
const getLocalModulePath = (opts) => join$1(opts.rootDir, "node_modules", opts.moduleId, opts.path);
|
|
2380
2382
|
const getRemoteModuleUrl = (opts) => {
|
|
2381
2383
|
const npmBaseUrl = "https://cdn.jsdelivr.net/npm/";
|
|
@@ -2391,13 +2393,14 @@ const createSystem = (c) => {
|
|
|
2391
2393
|
access,
|
|
2392
2394
|
accessSync,
|
|
2393
2395
|
addDestroy,
|
|
2396
|
+
checkVersion: async () => noop,
|
|
2394
2397
|
copyFile,
|
|
2395
2398
|
createDir,
|
|
2396
2399
|
createDirSync,
|
|
2397
2400
|
homeDir,
|
|
2398
2401
|
isTTY,
|
|
2399
|
-
getEnvironmentVar,
|
|
2400
2402
|
destroy,
|
|
2403
|
+
dynamicImport: (p) => import(p),
|
|
2401
2404
|
encodeToBase64,
|
|
2402
2405
|
exit: async (exitCode) => logger.warn(`exit ${exitCode}`),
|
|
2403
2406
|
getCurrentDirectory,
|
|
@@ -2408,6 +2411,7 @@ const createSystem = (c) => {
|
|
|
2408
2411
|
isSymbolicLink,
|
|
2409
2412
|
nextTick,
|
|
2410
2413
|
normalizePath: normalize,
|
|
2414
|
+
onProcessInterrupt: noop,
|
|
2411
2415
|
platformPath: path,
|
|
2412
2416
|
readDir,
|
|
2413
2417
|
readDirSync,
|
|
@@ -2417,7 +2421,6 @@ const createSystem = (c) => {
|
|
|
2417
2421
|
realpathSync,
|
|
2418
2422
|
removeDestroy,
|
|
2419
2423
|
rename,
|
|
2420
|
-
fetch,
|
|
2421
2424
|
resolvePath,
|
|
2422
2425
|
removeDir,
|
|
2423
2426
|
removeDirSync,
|
|
@@ -2432,6 +2435,7 @@ const createSystem = (c) => {
|
|
|
2432
2435
|
writeFile,
|
|
2433
2436
|
writeFileSync,
|
|
2434
2437
|
generateContentHash,
|
|
2438
|
+
generateFileHash,
|
|
2435
2439
|
createWorkerController: null,
|
|
2436
2440
|
details: {
|
|
2437
2441
|
cpuModel: "",
|
|
@@ -2633,7 +2637,8 @@ const getTranspileCssConfig = (compileOpts, importData, results) => {
|
|
|
2633
2637
|
minify: false,
|
|
2634
2638
|
autoprefixer: false,
|
|
2635
2639
|
module: compileOpts.module,
|
|
2636
|
-
styleImportData: compileOpts.styleImportData
|
|
2640
|
+
styleImportData: compileOpts.styleImportData,
|
|
2641
|
+
docs: compileOpts.docs === true
|
|
2637
2642
|
};
|
|
2638
2643
|
};
|
|
2639
2644
|
const getTranspileConfigOpt = (value, validValues, defaultValue) => {
|
|
@@ -3789,10 +3794,11 @@ const validateConfig = (userConfig = {}, bootstrapConfig) => {
|
|
|
3789
3794
|
const devMode = isBoolean$1(config.devMode) ? config.devMode : false;
|
|
3790
3795
|
config._isTesting = !!(process.env.VITEST || process.env.PLAYWRIGHT_TEST || process.env.TEST_WORKER_INDEX || process.env.TEST_PARALLEL_INDEX || false);
|
|
3791
3796
|
const sys = config.sys ?? bootstrapConfig.sys ?? createNodeSys({ logger });
|
|
3797
|
+
const hasExplicitGlobalStyleWithInput = (config.outputTargets || []).some((o) => isOutputTargetGlobalStyle(o) && isString$1(o.input));
|
|
3792
3798
|
if ((!isString$1(config.globalStyle) || !isString$1(config.globalScript)) && typeof sys.accessSync === "function") {
|
|
3793
3799
|
const preRootDir = typeof config.rootDir !== "string" ? "/" : config.rootDir;
|
|
3794
3800
|
const srcDir = typeof config.srcDir === "string" ? isAbsolute(config.srcDir) ? config.srcDir : join(preRootDir, config.srcDir) : join(preRootDir, "src");
|
|
3795
|
-
if (!isString$1(config.globalStyle)) for (const ext of STYLE_EXT) {
|
|
3801
|
+
if (!isString$1(config.globalStyle) && !hasExplicitGlobalStyleWithInput) for (const ext of STYLE_EXT) {
|
|
3796
3802
|
const candidate = join(srcDir, `global.${ext}`);
|
|
3797
3803
|
if (sys.accessSync(candidate)) {
|
|
3798
3804
|
config.globalStyle = candidate;
|
|
@@ -3812,7 +3818,8 @@ const validateConfig = (userConfig = {}, bootstrapConfig) => {
|
|
|
3812
3818
|
...config,
|
|
3813
3819
|
devMode,
|
|
3814
3820
|
compat: config.compat || {},
|
|
3815
|
-
generateExportMaps:
|
|
3821
|
+
generateExportMaps: config.generateExportMaps === true,
|
|
3822
|
+
enableCssOnlyComponents: config.enableCssOnlyComponents ?? true,
|
|
3816
3823
|
hydratedFlag: validateHydrated(config),
|
|
3817
3824
|
logLevel,
|
|
3818
3825
|
logger,
|
|
@@ -3870,29 +3877,137 @@ const validateConfig = (userConfig = {}, bootstrapConfig) => {
|
|
|
3870
3877
|
};
|
|
3871
3878
|
};
|
|
3872
3879
|
//#endregion
|
|
3880
|
+
//#region src/compiler/docs/css-doc-comments.ts
|
|
3881
|
+
/**
|
|
3882
|
+
* A JSDoc-style comment is `/** ... *\/` or `/*! ... *\/` - postcss's `Comment.text` already
|
|
3883
|
+
* strips the `/*`/`*\/` delimiters, so this checks the leading `*` or `!` that remains.
|
|
3884
|
+
* @param commentText a postcss `Comment` node's `.text`
|
|
3885
|
+
* @returns `true` if the comment is JSDoc-style
|
|
3886
|
+
*/
|
|
3887
|
+
const isJsDocComment = (commentText) => commentText.startsWith("*") || commentText.startsWith("!");
|
|
3888
|
+
/**
|
|
3889
|
+
* Strip the leading `*`/`!` marker and per-line `*` prefixes, returning trimmed lines.
|
|
3890
|
+
* @param commentText a postcss `Comment` node's `.text`
|
|
3891
|
+
* @returns the comment's lines, delimiters and leading `*`s stripped
|
|
3892
|
+
*/
|
|
3893
|
+
const normalizeJsDocLines = (commentText) => {
|
|
3894
|
+
return commentText.replace(/^[*!]/, "").split(/\r?\n/).map((line) => line.trim().replace(/^\*/, "").trim()).filter((line, i, arr) => !(line === "" && (i === 0 || i === arr.length - 1)));
|
|
3895
|
+
};
|
|
3896
|
+
/**
|
|
3897
|
+
* @param commentText a postcss `Comment` node's `.text`
|
|
3898
|
+
* @returns the comment's lines joined into a single trimmed line of text
|
|
3899
|
+
*/
|
|
3900
|
+
const normalizeJsDocText = (commentText) => normalizeJsDocLines(commentText).filter(Boolean).join(" ").trim();
|
|
3901
|
+
/**
|
|
3902
|
+
* @param value a CSS descriptor value, possibly quoted (e.g. a `syntax` descriptor's `"<length>"`)
|
|
3903
|
+
* @returns the value with any surrounding quotes removed
|
|
3904
|
+
*/
|
|
3905
|
+
const stripQuotes = (value) => value.replace(/^['"]|['"]$/g, "");
|
|
3906
|
+
/**
|
|
3907
|
+
* Extract the `syntax`/`initial-value` descriptors from a native `@property` at-rule's body.
|
|
3908
|
+
* @param node a postcss `AtRule` node whose `name` is `property`
|
|
3909
|
+
* @returns the extracted descriptors, if present
|
|
3910
|
+
*/
|
|
3911
|
+
const parsePropertyAtRuleDescriptors = (node) => {
|
|
3912
|
+
let syntax;
|
|
3913
|
+
let initialValue;
|
|
3914
|
+
node.each((child) => {
|
|
3915
|
+
if (child.type !== "decl") return;
|
|
3916
|
+
if (child.prop === "syntax") syntax = stripQuotes(child.value);
|
|
3917
|
+
else if (child.prop === "initial-value") initialValue = child.value;
|
|
3918
|
+
});
|
|
3919
|
+
return {
|
|
3920
|
+
syntax,
|
|
3921
|
+
initialValue
|
|
3922
|
+
};
|
|
3923
|
+
};
|
|
3924
|
+
//#endregion
|
|
3873
3925
|
//#region src/compiler/docs/style-docs.ts
|
|
3874
3926
|
/**
|
|
3875
3927
|
* Parse CSS docstrings that Stencil supports, as documented here:
|
|
3876
3928
|
* https://stenciljs.com/docs/docs-json#css-variables
|
|
3877
3929
|
*
|
|
3878
|
-
* Docstrings found in the supplied style text will be added to the
|
|
3879
|
-
*
|
|
3930
|
+
* Docstrings found in the supplied style text will be added to the `styleDocs` param, via three
|
|
3931
|
+
* mechanisms, in order of precedence (an earlier mechanism always wins over a later one for the
|
|
3932
|
+
* same custom property name):
|
|
3933
|
+
* 1. An explicit `@prop`/`@cssprop`/`@cssproperty` annotation in a block comment, anywhere in the file.
|
|
3934
|
+
* 2. A doc-commented `--foo: value;` declaration inside the component's "root" rule - `:host`
|
|
3935
|
+
* for `shadow`/`scoped` encapsulation, or the component's own tag selector for `none`.
|
|
3936
|
+
* 3. A native `@property --foo { ... }` at-rule, with the `syntax`/`initial-value` descriptors
|
|
3937
|
+
* surfaced as `syntax`/`default`. Not scoped - `@property` is a global registration.
|
|
3880
3938
|
*
|
|
3881
3939
|
* @param styleDocs the array to hold formatted CSS docstrings
|
|
3882
3940
|
* @param styleText the CSS text we're working with
|
|
3883
3941
|
* @param mode a mode associated with the parsed style, if applicable (e.g. this is not applicable for global styles)
|
|
3942
|
+
* @param tag the component's tag name, used to scope auto-detection for `none` encapsulation
|
|
3943
|
+
* @param encapsulation the component's encapsulation, used to pick the "root" scope for auto-detection
|
|
3884
3944
|
*/
|
|
3885
|
-
function parseStyleDocs(styleDocs, styleText, mode) {
|
|
3945
|
+
function parseStyleDocs(styleDocs, styleText, mode, tag, encapsulation) {
|
|
3886
3946
|
if (typeof styleText !== "string") return;
|
|
3887
|
-
let
|
|
3947
|
+
let remainingText = styleText;
|
|
3948
|
+
let match = remainingText.match(CSS_DOC_START);
|
|
3888
3949
|
while (match !== null) {
|
|
3889
|
-
|
|
3890
|
-
const endIndex =
|
|
3950
|
+
remainingText = remainingText.substring(match.index + match[0].length);
|
|
3951
|
+
const endIndex = remainingText.indexOf(CSS_DOC_END);
|
|
3891
3952
|
if (endIndex === -1) break;
|
|
3892
|
-
parseCssComment(styleDocs,
|
|
3893
|
-
|
|
3894
|
-
match =
|
|
3953
|
+
parseCssComment(styleDocs, remainingText.substring(0, endIndex), mode);
|
|
3954
|
+
remainingText = remainingText.substring(endIndex + CSS_DOC_END.length);
|
|
3955
|
+
match = remainingText.match(CSS_DOC_START);
|
|
3895
3956
|
}
|
|
3957
|
+
if (styleText.includes("--")) parseScopedAndNativeStyleDocs(styleDocs, styleText, mode, tag, encapsulation);
|
|
3958
|
+
}
|
|
3959
|
+
/**
|
|
3960
|
+
* Auto-detect doc-commented custom-property declarations within the component's "root" rule,
|
|
3961
|
+
* and native `@property` at-rule registrations, merging them into `styleDocs`. Mirrors the
|
|
3962
|
+
* equivalent detection for CSS-only components in `css-components/parse-css-component.ts`.
|
|
3963
|
+
*
|
|
3964
|
+
* @param styleDocs the array to hold formatted CSS docstrings - also consulted so an explicit
|
|
3965
|
+
* `@prop`/`@cssprop` annotation always wins over an auto-detected entry for the same name
|
|
3966
|
+
* @param styleText the CSS text we're working with
|
|
3967
|
+
* @param mode a mode associated with the parsed style, if applicable
|
|
3968
|
+
* @param tag the component's tag name
|
|
3969
|
+
* @param encapsulation the component's encapsulation
|
|
3970
|
+
*/
|
|
3971
|
+
function parseScopedAndNativeStyleDocs(styleDocs, styleText, mode, tag, encapsulation) {
|
|
3972
|
+
let root;
|
|
3973
|
+
try {
|
|
3974
|
+
root = postcss().process(styleText, { from: void 0 }).root;
|
|
3975
|
+
} catch {
|
|
3976
|
+
return;
|
|
3977
|
+
}
|
|
3978
|
+
const hasName = (name) => styleDocs.some((styleDoc) => styleDoc.name === name);
|
|
3979
|
+
const rootSelector = encapsulation === "shadow" || encapsulation === "scoped" ? ":host" : tag;
|
|
3980
|
+
if (rootSelector) root.walkRules((rule) => {
|
|
3981
|
+
if (!rule.selectors.some((selector) => selector.trim() === rootSelector)) return;
|
|
3982
|
+
const children = rule.nodes;
|
|
3983
|
+
for (let i = 0; i < children.length; i++) {
|
|
3984
|
+
const child = children[i];
|
|
3985
|
+
if (child.type !== "decl" || !child.prop.startsWith("--") || hasName(child.prop)) continue;
|
|
3986
|
+
const prev = children[i - 1];
|
|
3987
|
+
if (!prev || prev.type !== "comment" || !isJsDocComment(prev.text)) continue;
|
|
3988
|
+
styleDocs.push({
|
|
3989
|
+
name: child.prop,
|
|
3990
|
+
docs: normalizeJsDocText(prev.text),
|
|
3991
|
+
annotation: "prop",
|
|
3992
|
+
mode
|
|
3993
|
+
});
|
|
3994
|
+
}
|
|
3995
|
+
});
|
|
3996
|
+
root.walkAtRules("property", (node) => {
|
|
3997
|
+
const propName = node.params.trim();
|
|
3998
|
+
if (!propName.startsWith("--") || hasName(propName)) return;
|
|
3999
|
+
const { syntax, initialValue } = parsePropertyAtRuleDescriptors(node);
|
|
4000
|
+
const prev = node.prev();
|
|
4001
|
+
const docs = prev && prev.type === "comment" && isJsDocComment(prev.text) ? normalizeJsDocText(prev.text) : "";
|
|
4002
|
+
styleDocs.push({
|
|
4003
|
+
name: propName,
|
|
4004
|
+
docs,
|
|
4005
|
+
annotation: "prop",
|
|
4006
|
+
mode,
|
|
4007
|
+
...syntax && { syntax },
|
|
4008
|
+
...initialValue && { default: initialValue }
|
|
4009
|
+
});
|
|
4010
|
+
});
|
|
3896
4011
|
}
|
|
3897
4012
|
/**
|
|
3898
4013
|
* Parse a CSS comment string and insert it into the provided array of
|
|
@@ -3909,7 +4024,7 @@ function parseCssComment(styleDocs, comment, mode) {
|
|
|
3909
4024
|
return line;
|
|
3910
4025
|
}).join(" ").replace(/\t/g, " ").trim();
|
|
3911
4026
|
while (comment.includes(" ")) comment = comment.replace(" ", " ");
|
|
3912
|
-
comment.split(
|
|
4027
|
+
comment.split(CSS_PROP_ANNOTATION_RE).forEach((d) => {
|
|
3913
4028
|
const cssDocument = d.trim();
|
|
3914
4029
|
if (!cssDocument.startsWith(`--`)) return;
|
|
3915
4030
|
const splt = cssDocument.split(`:`);
|
|
@@ -3932,9 +4047,11 @@ const CSS_DOC_START = /\/\*(\*|!)/;
|
|
|
3932
4047
|
*/
|
|
3933
4048
|
const CSS_DOC_END = "*/";
|
|
3934
4049
|
/**
|
|
3935
|
-
* The `@prop` annotation we support within CSS docstrings
|
|
4050
|
+
* The `@prop` annotation we support within CSS docstrings. `@cssprop`/`@cssproperty` are also
|
|
4051
|
+
* recognized as synonyms (the more common names in other web-component-analyzer/docgen
|
|
4052
|
+
* ecosystems).
|
|
3936
4053
|
*/
|
|
3937
|
-
const
|
|
4054
|
+
const CSS_PROP_ANNOTATION_RE = /@(?:cssproperty|cssprop|prop)\b/;
|
|
3938
4055
|
//#endregion
|
|
3939
4056
|
//#region src/compiler/sys/browser-stubs/browserslist.ts
|
|
3940
4057
|
const browserslist = (_query) => [];
|
|
@@ -4046,6 +4163,39 @@ const DEFAULT_BROWSER_TARGETS = [
|
|
|
4046
4163
|
//#endregion
|
|
4047
4164
|
//#region src/compiler/style/css-parser/parse-css.ts
|
|
4048
4165
|
/**
|
|
4166
|
+
* Splits a selector list on top-level commas in a single pass: a comma only
|
|
4167
|
+
* splits when it's outside a quoted string and at paren depth 0, so commas in
|
|
4168
|
+
* `:is(:a,:has(:b))` or `[title="a,b"]` aren't treated as separators.
|
|
4169
|
+
* Backslash escapes (inside or outside a string) are skipped so they can't shift state,
|
|
4170
|
+
* so e.g. `[title="a\,b"]` is treated as a single selector.
|
|
4171
|
+
* @param selectors The selector list to split
|
|
4172
|
+
* @returns An array of selectors, with whitespace trimmed from each selector
|
|
4173
|
+
*/
|
|
4174
|
+
const splitSelectorList = (selectors) => {
|
|
4175
|
+
const parts = [];
|
|
4176
|
+
let depth = 0;
|
|
4177
|
+
let quote = null;
|
|
4178
|
+
let start = 0;
|
|
4179
|
+
for (let i = 0; i < selectors.length; i++) {
|
|
4180
|
+
const ch = selectors[i];
|
|
4181
|
+
if (quote) {
|
|
4182
|
+
if (ch === "\\") i++;
|
|
4183
|
+
else if (ch === quote) quote = null;
|
|
4184
|
+
continue;
|
|
4185
|
+
}
|
|
4186
|
+
if (ch === "\"" || ch === "'") quote = ch;
|
|
4187
|
+
else if (ch === "\\") i++;
|
|
4188
|
+
else if (ch === "(") depth++;
|
|
4189
|
+
else if (ch === ")") depth = Math.max(0, depth - 1);
|
|
4190
|
+
else if (ch === "," && depth === 0) {
|
|
4191
|
+
parts.push(selectors.slice(start, i));
|
|
4192
|
+
start = i + 1;
|
|
4193
|
+
}
|
|
4194
|
+
}
|
|
4195
|
+
parts.push(selectors.slice(start));
|
|
4196
|
+
return parts.map((s) => s.trim());
|
|
4197
|
+
};
|
|
4198
|
+
/**
|
|
4049
4199
|
* Parses CSS string input into an AST representation.
|
|
4050
4200
|
* Used for minification, finding & resolving URLs and during SSR / prerendering, removing unused selectors.
|
|
4051
4201
|
*
|
|
@@ -4170,11 +4320,8 @@ const parseCss = (css, filePath) => {
|
|
|
4170
4320
|
const selector = () => {
|
|
4171
4321
|
const m = match(/^([^{]+)/);
|
|
4172
4322
|
if (!m) return null;
|
|
4173
|
-
|
|
4174
|
-
|
|
4175
|
-
}).split(/\s*(?![^(]*\)),\s*/).map(function(s) {
|
|
4176
|
-
return s.replace(/\u200C/g, ",");
|
|
4177
|
-
});
|
|
4323
|
+
const cleaned = trim(m[0]).replace(/\/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*\/+/g, "");
|
|
4324
|
+
return splitSelectorList(cleaned);
|
|
4178
4325
|
};
|
|
4179
4326
|
const declaration = () => {
|
|
4180
4327
|
const pos = position();
|
|
@@ -4772,12 +4919,10 @@ const optimizeCss = async (inputOpts) => {
|
|
|
4772
4919
|
const CREATE_EVENT = "__stencil_createEvent";
|
|
4773
4920
|
const DEFINE_CUSTOM_ELEMENT = "__stencil_defineCustomElement";
|
|
4774
4921
|
const GET_ELEMENT = "__stencil_getElement";
|
|
4775
|
-
const HOST = "__stencil_Host";
|
|
4776
4922
|
const HTML_ELEMENT = "HTMLElement";
|
|
4777
4923
|
const PROXY_CUSTOM_ELEMENT = "__stencil_proxyCustomElement";
|
|
4778
4924
|
const REGISTER_INSTANCE = "__stencil_registerInstance";
|
|
4779
4925
|
const REGISTER_HOST = "__stencil_registerHost";
|
|
4780
|
-
const H = "__stencil_h";
|
|
4781
4926
|
const TRANSFORM_TAG = "__stencil_transformTag";
|
|
4782
4927
|
const GET_REGISTRY = "__stencil_getRegistry";
|
|
4783
4928
|
const INJECT_SIDE_EFFECT_STYLE = "__stencil_injectSideEffectStyle";
|
|
@@ -4785,9 +4930,7 @@ const RUNTIME_APIS = {
|
|
|
4785
4930
|
createEvent: `createEvent as ${CREATE_EVENT}`,
|
|
4786
4931
|
defineCustomElement: `defineCustomElement as ${DEFINE_CUSTOM_ELEMENT}`,
|
|
4787
4932
|
getElement: `getElement as ${GET_ELEMENT}`,
|
|
4788
|
-
h: `h as ${H}`,
|
|
4789
4933
|
legacyH: `h`,
|
|
4790
|
-
Host: `Host as ${HOST}`,
|
|
4791
4934
|
HTMLElement: HTML_ELEMENT,
|
|
4792
4935
|
proxyCustomElement: `proxyCustomElement as ${PROXY_CUSTOM_ELEMENT}`,
|
|
4793
4936
|
registerHost: `registerHost as ${REGISTER_HOST}`,
|
|
@@ -9186,9 +9329,9 @@ const serializeSymbol = (checker, symbol) => {
|
|
|
9186
9329
|
};
|
|
9187
9330
|
};
|
|
9188
9331
|
/**
|
|
9189
|
-
* Maps a TypeScript 4.3+ JSDocTagInfo to a flattened Stencil
|
|
9332
|
+
* Maps a TypeScript 4.3+ JSDocTagInfo to a flattened Stencil JsonDocsTag.
|
|
9190
9333
|
* @param tags A readonly array of JSDocTagInfo objects.
|
|
9191
|
-
* @returns An array of
|
|
9334
|
+
* @returns An array of JsonDocsTag objects.
|
|
9192
9335
|
*/
|
|
9193
9336
|
const mapJSDocTagInfo = (tags) => {
|
|
9194
9337
|
return tags.map((tag) => ({
|
|
@@ -9599,7 +9742,7 @@ const transformCssToEsmModule = (input) => {
|
|
|
9599
9742
|
defaultVarName: createCssVarName(input.file, input.mode),
|
|
9600
9743
|
styleDocs: []
|
|
9601
9744
|
};
|
|
9602
|
-
if (input.docs) parseStyleDocs(results.styleDocs, input.input, input.mode);
|
|
9745
|
+
if (input.docs) parseStyleDocs(results.styleDocs, input.input, input.mode, input.tag, input.encapsulation);
|
|
9603
9746
|
try {
|
|
9604
9747
|
const varNames = /* @__PURE__ */ new Set([results.defaultVarName]);
|
|
9605
9748
|
if (isString$1(input.tag) && input.encapsulation === "scoped") {
|
|
@@ -9696,7 +9839,7 @@ const getCssToEsmImports = (varNames, cssText, filePath, modeName) => {
|
|
|
9696
9839
|
isNodeModule: false
|
|
9697
9840
|
};
|
|
9698
9841
|
if (!isLocalCssImport(cssImportData.srcImportText)) continue;
|
|
9699
|
-
else if (cssImportData.url === "stencil-globals" || cssImportData.url === "stencil-hydrate") continue;
|
|
9842
|
+
else if (cssImportData.url === "stencil-component-globals" || cssImportData.url === "stencil-hydrate" || cssImportData.url === "stencil-css-components") continue;
|
|
9700
9843
|
else if (isCssNodeModule(cssImportData.url)) {
|
|
9701
9844
|
cssImportData.filePath = cssImportData.url.substring(1);
|
|
9702
9845
|
cssImportData.isNodeModule = true;
|
|
@@ -9911,6 +10054,7 @@ var BuildContext = class {
|
|
|
9911
10054
|
completedTasks = [];
|
|
9912
10055
|
compilerCtx;
|
|
9913
10056
|
components = [];
|
|
10057
|
+
cssOnlyComponents = [];
|
|
9914
10058
|
componentGraph = /* @__PURE__ */ new Map();
|
|
9915
10059
|
config;
|
|
9916
10060
|
data = {};
|
|
@@ -9952,6 +10096,7 @@ var BuildContext = class {
|
|
|
9952
10096
|
styleBuildCount = 0;
|
|
9953
10097
|
stylesPromise = null;
|
|
9954
10098
|
stylesUpdated = [];
|
|
10099
|
+
globalStylesUpdated = [];
|
|
9955
10100
|
timeSpan = null;
|
|
9956
10101
|
timestamp;
|
|
9957
10102
|
transpileBuildCount = 0;
|
|
@@ -10132,12 +10277,18 @@ var CompilerContext = class {
|
|
|
10132
10277
|
*/
|
|
10133
10278
|
prevStylesMap = /* @__PURE__ */ new Map();
|
|
10134
10279
|
globalStyleCache = /* @__PURE__ */ new Map();
|
|
10280
|
+
cssOnlyComponentsCache = /* @__PURE__ */ new Map();
|
|
10281
|
+
stencilCss;
|
|
10282
|
+
globalStyleVirtualImports = /* @__PURE__ */ new Map();
|
|
10135
10283
|
styleModeNames = /* @__PURE__ */ new Set();
|
|
10136
10284
|
worker = null;
|
|
10137
10285
|
reset() {
|
|
10138
10286
|
this.cache.clear();
|
|
10139
10287
|
this.cssModuleImports.clear();
|
|
10140
10288
|
this.globalStyleCache.clear();
|
|
10289
|
+
this.cssOnlyComponentsCache.clear();
|
|
10290
|
+
this.stencilCss = void 0;
|
|
10291
|
+
this.globalStyleVirtualImports.clear();
|
|
10141
10292
|
this.collections.length = 0;
|
|
10142
10293
|
this.compilerOptions = null;
|
|
10143
10294
|
this.hasSuccessfulBuild = false;
|
|
@@ -10742,31 +10893,6 @@ const createIdentifierFromStyleIdentifier = (styleIdentifier, externalStyleIds)
|
|
|
10742
10893
|
return ts$1.factory.createBinaryExpression(createIdentifierFromStyleIdentifier(styleIdentifier, [id]), ts$1.SyntaxKind.PlusToken, createIdentifierFromStyleIdentifier(styleIdentifier, externalStyleIds.slice(1)));
|
|
10743
10894
|
};
|
|
10744
10895
|
//#endregion
|
|
10745
|
-
//#region src/compiler/transformers/host-data-transform.ts
|
|
10746
|
-
const transformHostData = (classElements, moduleFile) => {
|
|
10747
|
-
if (classElements.some((e) => ts$1.isMethodDeclaration(e) && e.name.escapedText === "hostData")) {
|
|
10748
|
-
const renderIndex = classElements.findIndex((e) => ts$1.isMethodDeclaration(e) && e.name.escapedText === "render");
|
|
10749
|
-
if (renderIndex >= 0) {
|
|
10750
|
-
const renderMethod = classElements[renderIndex];
|
|
10751
|
-
classElements[renderIndex] = ts$1.factory.updateMethodDeclaration(renderMethod, retrieveModifierLike(renderMethod), renderMethod.asteriskToken, ts$1.factory.createIdentifier(INTERNAL_RENDER), renderMethod.questionToken, renderMethod.typeParameters, renderMethod.parameters, renderMethod.type, renderMethod.body);
|
|
10752
|
-
}
|
|
10753
|
-
classElements.push(syntheticRender(moduleFile, renderIndex >= 0));
|
|
10754
|
-
}
|
|
10755
|
-
};
|
|
10756
|
-
const syntheticRender = (moduleFile, hasRender) => {
|
|
10757
|
-
addCoreRuntimeApi(moduleFile, RUNTIME_APIS.Host);
|
|
10758
|
-
addCoreRuntimeApi(moduleFile, RUNTIME_APIS.h);
|
|
10759
|
-
const hArguments = [ts$1.factory.createIdentifier(HOST), ts$1.factory.createCallExpression(ts$1.factory.createPropertyAccessExpression(ts$1.factory.createThis(), "hostData"), void 0, void 0)];
|
|
10760
|
-
if (hasRender) hArguments.push(ts$1.factory.createCallExpression(ts$1.factory.createPropertyAccessExpression(ts$1.factory.createThis(), INTERNAL_RENDER), void 0, void 0));
|
|
10761
|
-
/**
|
|
10762
|
-
* render() {
|
|
10763
|
-
* return h(arguments);
|
|
10764
|
-
* }
|
|
10765
|
-
*/
|
|
10766
|
-
return ts$1.factory.createMethodDeclaration(void 0, void 0, "render", void 0, void 0, [], void 0, ts$1.factory.createBlock([ts$1.factory.createReturnStatement(ts$1.factory.createCallExpression(ts$1.factory.createIdentifier(H), void 0, hArguments))]));
|
|
10767
|
-
};
|
|
10768
|
-
const INTERNAL_RENDER = "__stencil_render";
|
|
10769
|
-
//#endregion
|
|
10770
10896
|
//#region src/compiler/transformers/reactive-handler-meta-transform.ts
|
|
10771
10897
|
/**
|
|
10772
10898
|
* Add a getter to a class for a static representation of the watchers
|
|
@@ -10848,7 +10974,6 @@ const updateLazyComponentMembers = (transformOpts, styleStatements, classNode, m
|
|
|
10848
10974
|
addReactivePropHandlers(classMembers, cmp, "watchers");
|
|
10849
10975
|
addReactivePropHandlers(classMembers, cmp, "serializers");
|
|
10850
10976
|
addReactivePropHandlers(classMembers, cmp, "deserializers");
|
|
10851
|
-
transformHostData(classMembers, moduleFile);
|
|
10852
10977
|
if (transformOpts.style === "static") addStaticStylePropertyToClass(styleStatements, cmp, buildCtx);
|
|
10853
10978
|
return classMembers;
|
|
10854
10979
|
};
|
|
@@ -11296,7 +11421,6 @@ const updateNativeHostComponentMembers = (transformOpts, classNode, moduleFile,
|
|
|
11296
11421
|
addReactivePropHandlers(classMembers, cmp, "deserializers");
|
|
11297
11422
|
if (cmp.isPlain) addNativeComponentMeta(classMembers, cmp);
|
|
11298
11423
|
if (transformOpts.style === "static") addNativeStaticStyle(classMembers, cmp, buildCtx);
|
|
11299
|
-
transformHostData(classMembers, moduleFile);
|
|
11300
11424
|
return classMembers;
|
|
11301
11425
|
};
|
|
11302
11426
|
//#endregion
|
|
@@ -11499,110 +11623,6 @@ const isDecoratorNamed = (propName) => {
|
|
|
11499
11623
|
};
|
|
11500
11624
|
};
|
|
11501
11625
|
//#endregion
|
|
11502
|
-
//#region src/compiler/transformers/decorators-to-static/attach-internals.ts
|
|
11503
|
-
/**
|
|
11504
|
-
* Convert the attach internals decorator to static, saving the name of the
|
|
11505
|
-
* decorated property so an `ElementInternals` object can be bound to it later
|
|
11506
|
-
* on.
|
|
11507
|
-
*
|
|
11508
|
-
* The `@AttachInternals` decorator is used to indicate a field on a class
|
|
11509
|
-
* where the return value of the `HTMLElement.attachInternals` method should be
|
|
11510
|
-
* bound. This then allows component authors to use that interface to make their
|
|
11511
|
-
* Stencil components rich participants in whatever `HTMLFormElement` instances
|
|
11512
|
-
* they find themselves inside of in the future.
|
|
11513
|
-
*
|
|
11514
|
-
* The decorator also accepts an optional `states` option to define initial
|
|
11515
|
-
* custom states that will be set on the `ElementInternals.states` CustomStateSet.
|
|
11516
|
-
* Each state property can have a JSDoc comment that will be extracted for documentation.
|
|
11517
|
-
*
|
|
11518
|
-
* **Note**: this function will mutate the `newMembers` parameter in order to
|
|
11519
|
-
* add new members to the class.
|
|
11520
|
-
*
|
|
11521
|
-
* @param diagnostics for reporting errors and warnings
|
|
11522
|
-
* @param decoratedMembers the decorated members found on the class
|
|
11523
|
-
* @param newMembers an out param for new class members
|
|
11524
|
-
* @param typeChecker a TypeScript typechecker, needed for resolving the prop
|
|
11525
|
-
* declaration name
|
|
11526
|
-
* @param decoratorName the name of the decorator to look for
|
|
11527
|
-
*/
|
|
11528
|
-
const attachInternalsDecoratorsToStatic = (diagnostics, decoratedMembers, newMembers, typeChecker, decoratorName) => {
|
|
11529
|
-
const attachInternalsMembers = decoratedMembers.filter(ts$1.isPropertyDeclaration).filter((prop) => {
|
|
11530
|
-
return !!retrieveTsDecorators(prop)?.find(isDecoratorNamed(decoratorName));
|
|
11531
|
-
});
|
|
11532
|
-
if (attachInternalsMembers.length === 0) return;
|
|
11533
|
-
if (attachInternalsMembers.length > 1) {
|
|
11534
|
-
const error = buildError(diagnostics);
|
|
11535
|
-
error.messageText = `Stencil does not support adding more than one AttachInternals() decorator to a component`;
|
|
11536
|
-
return;
|
|
11537
|
-
}
|
|
11538
|
-
const [decoratedProp] = attachInternalsMembers;
|
|
11539
|
-
const { staticName: name } = tsPropDeclName(decoratedProp, typeChecker);
|
|
11540
|
-
const decorator = retrieveTsDecorators(decoratedProp)?.find(isDecoratorNamed(decoratorName));
|
|
11541
|
-
const customStates = parseCustomStatesFromDecorator(decorator, typeChecker);
|
|
11542
|
-
const formAssociated = parseFormAssociatedFromDecorator(decorator);
|
|
11543
|
-
newMembers.push(createStaticGetter("attachInternalsMemberName", convertValueToLiteral(name)));
|
|
11544
|
-
if (customStates.length > 0) newMembers.push(createStaticGetter("attachInternalsCustomStates", convertValueToLiteral(customStates)));
|
|
11545
|
-
if (formAssociated) newMembers.push(createStaticGetter("formAssociated", convertValueToLiteral(true)));
|
|
11546
|
-
};
|
|
11547
|
-
/**
|
|
11548
|
-
* Parse the formAssociated option from the decorator.
|
|
11549
|
-
* Returns true (form-associated) unless explicitly set to false.
|
|
11550
|
-
*
|
|
11551
|
-
* @param decorator the decorator node to parse
|
|
11552
|
-
* @returns whether the component should be form-associated
|
|
11553
|
-
*/
|
|
11554
|
-
function parseFormAssociatedFromDecorator(decorator) {
|
|
11555
|
-
if (!decorator || !ts$1.isCallExpression(decorator.expression)) return true;
|
|
11556
|
-
const [firstArg] = decorator.expression.arguments;
|
|
11557
|
-
if (!firstArg || !ts$1.isObjectLiteralExpression(firstArg)) return true;
|
|
11558
|
-
const formAssociatedProp = firstArg.properties.find((prop) => ts$1.isPropertyAssignment(prop) && ts$1.isIdentifier(prop.name) && prop.name.text === "formAssociated");
|
|
11559
|
-
if (!formAssociatedProp) return true;
|
|
11560
|
-
if (formAssociatedProp.initializer.kind === ts$1.SyntaxKind.FalseKeyword) return false;
|
|
11561
|
-
return true;
|
|
11562
|
-
}
|
|
11563
|
-
/**
|
|
11564
|
-
* Parse custom states from the decorator AST, including JSDoc comments.
|
|
11565
|
-
*
|
|
11566
|
-
* Supports JSDoc comments on state properties:
|
|
11567
|
-
* ```ts
|
|
11568
|
-
* @AttachInternals({
|
|
11569
|
-
* states: {
|
|
11570
|
-
* hovered: false,
|
|
11571
|
-
* /** Whether is currently active */
|
|
11572
|
-
* active: true
|
|
11573
|
-
* }
|
|
11574
|
-
* })
|
|
11575
|
-
* ```
|
|
11576
|
-
*
|
|
11577
|
-
* @param decorator the decorator node to parse
|
|
11578
|
-
* @param typeChecker a TypeScript typechecker for resolving symbols
|
|
11579
|
-
* @returns array of custom state metadata with docs
|
|
11580
|
-
*/
|
|
11581
|
-
function parseCustomStatesFromDecorator(decorator, typeChecker) {
|
|
11582
|
-
if (!decorator || !ts$1.isCallExpression(decorator.expression)) return [];
|
|
11583
|
-
const [firstArg] = decorator.expression.arguments;
|
|
11584
|
-
if (!firstArg || !ts$1.isObjectLiteralExpression(firstArg)) return [];
|
|
11585
|
-
const statesProp = firstArg.properties.find((prop) => ts$1.isPropertyAssignment(prop) && ts$1.isIdentifier(prop.name) && prop.name.text === "states");
|
|
11586
|
-
if (!statesProp || !ts$1.isObjectLiteralExpression(statesProp.initializer)) return [];
|
|
11587
|
-
const customStates = [];
|
|
11588
|
-
for (const prop of statesProp.initializer.properties) {
|
|
11589
|
-
if (!ts$1.isPropertyAssignment(prop)) continue;
|
|
11590
|
-
const stateName = ts$1.isIdentifier(prop.name) ? prop.name.text : ts$1.isStringLiteral(prop.name) ? prop.name.text : null;
|
|
11591
|
-
if (!stateName) continue;
|
|
11592
|
-
let initialValue = false;
|
|
11593
|
-
if (prop.initializer.kind === ts$1.SyntaxKind.TrueKeyword) initialValue = true;
|
|
11594
|
-
else if (prop.initializer.kind === ts$1.SyntaxKind.FalseKeyword) initialValue = false;
|
|
11595
|
-
const symbol = typeChecker.getSymbolAtLocation(prop.name);
|
|
11596
|
-
const docs = symbol ? ts$1.displayPartsToString(symbol.getDocumentationComment(typeChecker)) : "";
|
|
11597
|
-
customStates.push({
|
|
11598
|
-
name: stateName,
|
|
11599
|
-
initialValue,
|
|
11600
|
-
docs
|
|
11601
|
-
});
|
|
11602
|
-
}
|
|
11603
|
-
return customStates;
|
|
11604
|
-
}
|
|
11605
|
-
//#endregion
|
|
11606
11626
|
//#region src/compiler/transformers/decorators-to-static/style-to-static.ts
|
|
11607
11627
|
const styleToStatic = (newMembers, componentOptions) => {
|
|
11608
11628
|
const defaultModeStyles = [];
|
|
@@ -11667,6 +11687,12 @@ const normalizeStyleUrl = (style) => {
|
|
|
11667
11687
|
//#region src/compiler/transformers/decorators-to-static/component-decorator.ts
|
|
11668
11688
|
let hasShownDeprecatedApiError = false;
|
|
11669
11689
|
/**
|
|
11690
|
+
* Reset the deprecated API error flag. Call this at the start of each build.
|
|
11691
|
+
*/
|
|
11692
|
+
const resetDeprecatedApiWarning = () => {
|
|
11693
|
+
hasShownDeprecatedApiError = false;
|
|
11694
|
+
};
|
|
11695
|
+
/**
|
|
11670
11696
|
* Perform code generation to create new class members for a Stencil component
|
|
11671
11697
|
* which will drive the runtime functionality specified by various options
|
|
11672
11698
|
* passed to the `@Component` decorator.
|
|
@@ -11867,6 +11893,110 @@ const findTagNode = (propName, node) => {
|
|
|
11867
11893
|
return out;
|
|
11868
11894
|
};
|
|
11869
11895
|
//#endregion
|
|
11896
|
+
//#region src/compiler/transformers/decorators-to-static/attach-internals.ts
|
|
11897
|
+
/**
|
|
11898
|
+
* Convert the attach internals decorator to static, saving the name of the
|
|
11899
|
+
* decorated property so an `ElementInternals` object can be bound to it later
|
|
11900
|
+
* on.
|
|
11901
|
+
*
|
|
11902
|
+
* The `@AttachInternals` decorator is used to indicate a field on a class
|
|
11903
|
+
* where the return value of the `HTMLElement.attachInternals` method should be
|
|
11904
|
+
* bound. This then allows component authors to use that interface to make their
|
|
11905
|
+
* Stencil components rich participants in whatever `HTMLFormElement` instances
|
|
11906
|
+
* they find themselves inside of in the future.
|
|
11907
|
+
*
|
|
11908
|
+
* The decorator also accepts an optional `states` option to define initial
|
|
11909
|
+
* custom states that will be set on the `ElementInternals.states` CustomStateSet.
|
|
11910
|
+
* Each state property can have a JSDoc comment that will be extracted for documentation.
|
|
11911
|
+
*
|
|
11912
|
+
* **Note**: this function will mutate the `newMembers` parameter in order to
|
|
11913
|
+
* add new members to the class.
|
|
11914
|
+
*
|
|
11915
|
+
* @param diagnostics for reporting errors and warnings
|
|
11916
|
+
* @param decoratedMembers the decorated members found on the class
|
|
11917
|
+
* @param newMembers an out param for new class members
|
|
11918
|
+
* @param typeChecker a TypeScript typechecker, needed for resolving the prop
|
|
11919
|
+
* declaration name
|
|
11920
|
+
* @param decoratorName the name of the decorator to look for
|
|
11921
|
+
*/
|
|
11922
|
+
const attachInternalsDecoratorsToStatic = (diagnostics, decoratedMembers, newMembers, typeChecker, decoratorName) => {
|
|
11923
|
+
const attachInternalsMembers = decoratedMembers.filter(ts$1.isPropertyDeclaration).filter((prop) => {
|
|
11924
|
+
return !!retrieveTsDecorators(prop)?.find(isDecoratorNamed(decoratorName));
|
|
11925
|
+
});
|
|
11926
|
+
if (attachInternalsMembers.length === 0) return;
|
|
11927
|
+
if (attachInternalsMembers.length > 1) {
|
|
11928
|
+
const error = buildError(diagnostics);
|
|
11929
|
+
error.messageText = `Stencil does not support adding more than one AttachInternals() decorator to a component`;
|
|
11930
|
+
return;
|
|
11931
|
+
}
|
|
11932
|
+
const [decoratedProp] = attachInternalsMembers;
|
|
11933
|
+
const { staticName: name } = tsPropDeclName(decoratedProp, typeChecker);
|
|
11934
|
+
const decorator = retrieveTsDecorators(decoratedProp)?.find(isDecoratorNamed(decoratorName));
|
|
11935
|
+
const customStates = parseCustomStatesFromDecorator(decorator, typeChecker);
|
|
11936
|
+
const formAssociated = parseFormAssociatedFromDecorator(decorator);
|
|
11937
|
+
newMembers.push(createStaticGetter("attachInternalsMemberName", convertValueToLiteral(name)));
|
|
11938
|
+
if (customStates.length > 0) newMembers.push(createStaticGetter("attachInternalsCustomStates", convertValueToLiteral(customStates)));
|
|
11939
|
+
if (formAssociated) newMembers.push(createStaticGetter("formAssociated", convertValueToLiteral(true)));
|
|
11940
|
+
};
|
|
11941
|
+
/**
|
|
11942
|
+
* Parse the formAssociated option from the decorator.
|
|
11943
|
+
* Returns true (form-associated) unless explicitly set to false.
|
|
11944
|
+
*
|
|
11945
|
+
* @param decorator the decorator node to parse
|
|
11946
|
+
* @returns whether the component should be form-associated
|
|
11947
|
+
*/
|
|
11948
|
+
function parseFormAssociatedFromDecorator(decorator) {
|
|
11949
|
+
if (!decorator || !ts$1.isCallExpression(decorator.expression)) return true;
|
|
11950
|
+
const [firstArg] = decorator.expression.arguments;
|
|
11951
|
+
if (!firstArg || !ts$1.isObjectLiteralExpression(firstArg)) return true;
|
|
11952
|
+
const formAssociatedProp = firstArg.properties.find((prop) => ts$1.isPropertyAssignment(prop) && ts$1.isIdentifier(prop.name) && prop.name.text === "formAssociated");
|
|
11953
|
+
if (!formAssociatedProp) return true;
|
|
11954
|
+
if (formAssociatedProp.initializer.kind === ts$1.SyntaxKind.FalseKeyword) return false;
|
|
11955
|
+
return true;
|
|
11956
|
+
}
|
|
11957
|
+
/**
|
|
11958
|
+
* Parse custom states from the decorator AST, including JSDoc comments.
|
|
11959
|
+
*
|
|
11960
|
+
* Supports JSDoc comments on state properties:
|
|
11961
|
+
* ```ts
|
|
11962
|
+
* @AttachInternals({
|
|
11963
|
+
* states: {
|
|
11964
|
+
* hovered: false,
|
|
11965
|
+
* /** Whether is currently active */
|
|
11966
|
+
* active: true
|
|
11967
|
+
* }
|
|
11968
|
+
* })
|
|
11969
|
+
* ```
|
|
11970
|
+
*
|
|
11971
|
+
* @param decorator the decorator node to parse
|
|
11972
|
+
* @param typeChecker a TypeScript typechecker for resolving symbols
|
|
11973
|
+
* @returns array of custom state metadata with docs
|
|
11974
|
+
*/
|
|
11975
|
+
function parseCustomStatesFromDecorator(decorator, typeChecker) {
|
|
11976
|
+
if (!decorator || !ts$1.isCallExpression(decorator.expression)) return [];
|
|
11977
|
+
const [firstArg] = decorator.expression.arguments;
|
|
11978
|
+
if (!firstArg || !ts$1.isObjectLiteralExpression(firstArg)) return [];
|
|
11979
|
+
const statesProp = firstArg.properties.find((prop) => ts$1.isPropertyAssignment(prop) && ts$1.isIdentifier(prop.name) && prop.name.text === "states");
|
|
11980
|
+
if (!statesProp || !ts$1.isObjectLiteralExpression(statesProp.initializer)) return [];
|
|
11981
|
+
const customStates = [];
|
|
11982
|
+
for (const prop of statesProp.initializer.properties) {
|
|
11983
|
+
if (!ts$1.isPropertyAssignment(prop)) continue;
|
|
11984
|
+
const stateName = ts$1.isIdentifier(prop.name) ? prop.name.text : ts$1.isStringLiteral(prop.name) ? prop.name.text : null;
|
|
11985
|
+
if (!stateName) continue;
|
|
11986
|
+
let initialValue = false;
|
|
11987
|
+
if (prop.initializer.kind === ts$1.SyntaxKind.TrueKeyword) initialValue = true;
|
|
11988
|
+
else if (prop.initializer.kind === ts$1.SyntaxKind.FalseKeyword) initialValue = false;
|
|
11989
|
+
const symbol = typeChecker.getSymbolAtLocation(prop.name);
|
|
11990
|
+
const docs = symbol ? ts$1.displayPartsToString(symbol.getDocumentationComment(typeChecker)) : "";
|
|
11991
|
+
customStates.push({
|
|
11992
|
+
name: stateName,
|
|
11993
|
+
initialValue,
|
|
11994
|
+
docs
|
|
11995
|
+
});
|
|
11996
|
+
}
|
|
11997
|
+
return customStates;
|
|
11998
|
+
}
|
|
11999
|
+
//#endregion
|
|
11870
12000
|
//#region src/compiler/transformers/decorators-to-static/decorators-constants.ts
|
|
11871
12001
|
/**
|
|
11872
12002
|
* All the decorators supported by Stencil
|
|
@@ -13194,14 +13324,16 @@ const parseWatchDecorator = (typeChecker, method, decoratorName) => {
|
|
|
13194
13324
|
* @param diagnostics for surfacing errors and warnings
|
|
13195
13325
|
* @param typeChecker a TypeScript typechecker instance
|
|
13196
13326
|
* @param program a {@link ts.Program} object
|
|
13327
|
+
* @param includeClassExpressions also convert `ts.ClassExpression` nodes (e.g. a mixin factory's
|
|
13328
|
+
* `(Base) => class extends Base {}`), not just named `ts.ClassDeclaration`s
|
|
13197
13329
|
* @returns a TypeScript transformer factory which can be passed to
|
|
13198
13330
|
* TypeScript to transform source code during the compilation process
|
|
13199
13331
|
*/
|
|
13200
|
-
const convertDecoratorsToStatic = (config, diagnostics, typeChecker, program) => {
|
|
13332
|
+
const convertDecoratorsToStatic = (config, diagnostics, typeChecker, program, includeClassExpressions = false) => {
|
|
13201
13333
|
return (transformCtx) => {
|
|
13202
13334
|
let sourceFile;
|
|
13203
13335
|
const visit = (node) => {
|
|
13204
|
-
if (ts$1.isClassDeclaration(node)) return visitClassDeclaration(config, diagnostics, typeChecker, program, node, sourceFile);
|
|
13336
|
+
if (ts$1.isClassDeclaration(node) || includeClassExpressions && ts$1.isClassExpression(node)) return visitClassDeclaration(config, diagnostics, typeChecker, program, node, sourceFile);
|
|
13205
13337
|
return ts$1.visitEachChild(node, visit, transformCtx);
|
|
13206
13338
|
};
|
|
13207
13339
|
return (tsSourceFile) => {
|
|
@@ -13211,10 +13343,10 @@ const convertDecoratorsToStatic = (config, diagnostics, typeChecker, program) =>
|
|
|
13211
13343
|
};
|
|
13212
13344
|
};
|
|
13213
13345
|
/**
|
|
13214
|
-
* Visit {@link ts.ClassDeclaration} nodes as part of the tree-traversal
|
|
13346
|
+
* Visit {@link ts.ClassDeclaration}/{@link ts.ClassExpression} nodes as part of the tree-traversal
|
|
13215
13347
|
* required for {@link convertDecoratorsToStatic} to work.
|
|
13216
13348
|
*
|
|
13217
|
-
* If a given class
|
|
13349
|
+
* If a given class node is not decorated with `@Component` this
|
|
13218
13350
|
* function will simply return it as-is. If it _is_ decorated, then it will
|
|
13219
13351
|
*
|
|
13220
13352
|
* 1. Convert the arguments to the `@Component` decorator to static values.
|
|
@@ -13223,7 +13355,7 @@ const convertDecoratorsToStatic = (config, diagnostics, typeChecker, program) =>
|
|
|
13223
13355
|
* to static values.
|
|
13224
13356
|
* 3. Validate that the various options selected by the component author are
|
|
13225
13357
|
* valid, both individually and in combination.
|
|
13226
|
-
* 4. Return an updated class
|
|
13358
|
+
* 4. Return an updated class node which is ready to be used in
|
|
13227
13359
|
* the rest of the Stencil compilation pipeline (e.g. in output target
|
|
13228
13360
|
* generation).
|
|
13229
13361
|
*
|
|
@@ -13242,7 +13374,7 @@ const visitClassDeclaration = (config, diagnostics, typeChecker, program, classN
|
|
|
13242
13374
|
const decoratedMembers = classMembers.filter((member) => (retrieveTsDecorators(member)?.length ?? 0) > 0);
|
|
13243
13375
|
if (!decoratedMembers.length && !componentDecorator) return classNode;
|
|
13244
13376
|
const filteredMethodsAndFields = removeStencilMethodDecorators(Array.from(classMembers), diagnostics, importAliasMap);
|
|
13245
|
-
if (componentDecorator) componentDecoratorToStatic(config, typeChecker, diagnostics, classNode, filteredMethodsAndFields, componentDecorator);
|
|
13377
|
+
if (componentDecorator && ts$1.isClassDeclaration(classNode)) componentDecoratorToStatic(config, typeChecker, diagnostics, classNode, filteredMethodsAndFields, componentDecorator);
|
|
13246
13378
|
if (decoratedMembers.length > 0) {
|
|
13247
13379
|
const serializers = serializeDecoratorsToStatic(typeChecker, decoratedMembers, filteredMethodsAndFields, importAliasMap.get("PropSerialize"), "PropSerialize", importAliasMap.get("Prop"));
|
|
13248
13380
|
const deserializers = serializeDecoratorsToStatic(typeChecker, decoratedMembers, filteredMethodsAndFields, importAliasMap.get("AttrDeserialize"), "AttrDeserialize", importAliasMap.get("Prop"));
|
|
@@ -13258,7 +13390,8 @@ const visitClassDeclaration = (config, diagnostics, typeChecker, program, classN
|
|
|
13258
13390
|
validateMethods(diagnostics, classMembers);
|
|
13259
13391
|
const currentDecorators = retrieveTsDecorators(classNode);
|
|
13260
13392
|
const updatedClassFields = updateConstructor(classNode, filteredMethodsAndFields, []);
|
|
13261
|
-
|
|
13393
|
+
const updatedModifiers = [...filterDecorators(currentDecorators, CLASS_DECORATORS_TO_REMOVE.map((decorator) => importAliasMap.get(decorator))) ?? [], ...retrieveTsModifiers(classNode) ?? []];
|
|
13394
|
+
return ts$1.isClassDeclaration(classNode) ? ts$1.factory.updateClassDeclaration(classNode, updatedModifiers, classNode.name, classNode.typeParameters, classNode.heritageClauses, updatedClassFields) : ts$1.factory.updateClassExpression(classNode, updatedModifiers, classNode.name, classNode.typeParameters, classNode.heritageClauses, updatedClassFields);
|
|
13262
13395
|
};
|
|
13263
13396
|
/**
|
|
13264
13397
|
* Take a list of `ClassElement` AST nodes and remove any decorators from
|
|
@@ -13869,11 +14002,11 @@ const parseStaticWatchers = (staticMembers) => {
|
|
|
13869
14002
|
//#endregion
|
|
13870
14003
|
//#region src/compiler/transformers/static-to-meta/class-extension/shared.ts
|
|
13871
14004
|
/**
|
|
13872
|
-
* Warns when an `extends`/`Mixin(...)` target resolved to *something* but no class
|
|
13873
|
-
* could be found inside it - e.g. a mixin factory
|
|
13874
|
-
* (`(Base) =>
|
|
13875
|
-
*
|
|
13876
|
-
*
|
|
14005
|
+
* Warns when an `extends`/`Mixin(...)` target resolved to *something* but no class literal
|
|
14006
|
+
* could be found inside it - e.g. a mixin factory that returns an existing class by reference
|
|
14007
|
+
* (`(Base) => SomeExistingClass`) rather than a class literal. Without this, the target is
|
|
14008
|
+
* silently dropped: any `@Prop`/`@State` etc. it declares just never appears on the extending
|
|
14009
|
+
* component, with no diagnostic explaining why.
|
|
13877
14010
|
* @param buildCtx used to surface the warning - omit to warn silently (e.g. from tests)
|
|
13878
14011
|
* @param targetName the identifier the target was reached through
|
|
13879
14012
|
* @param anchor the node to attach the warning to
|
|
@@ -13881,27 +14014,32 @@ const parseStaticWatchers = (staticMembers) => {
|
|
|
13881
14014
|
function warnMixinFactoryClassNotFound(buildCtx, targetName, anchor) {
|
|
13882
14015
|
if (!buildCtx) return;
|
|
13883
14016
|
const err = buildWarn(buildCtx.diagnostics);
|
|
13884
|
-
err.messageText = `Found "${targetName}", but couldn't find a class
|
|
14017
|
+
err.messageText = `Found "${targetName}", but couldn't find a class inside it. If it's meant to be a mixin factory, make sure it returns a class literal directly, e.g. \`(Base) => class extends Base {}\` or \`(Base) => { class ${targetName}Class extends Base {} return ${targetName}Class; }\` - returning an existing class by reference (\`(Base) => SomeExistingClass\`) isn't recognized, and any \`@Prop\`/\`@State\`/etc. it declares won't be applied.`;
|
|
13885
14018
|
if (!buildCtx.config._isTesting) augmentDiagnosticWithNode(err, anchor);
|
|
13886
14019
|
}
|
|
13887
14020
|
/**
|
|
13888
|
-
* Walks the AST looking for a class declaration, optionally by name -
|
|
13889
|
-
* into a mixin factory's wrapping function (arrow function or `function`
|
|
13890
|
-
*
|
|
13891
|
-
*
|
|
14021
|
+
* Walks the AST looking for a class declaration or class expression, optionally by name -
|
|
14022
|
+
* descends into a mixin factory's wrapping function (arrow function or `function` declaration)
|
|
14023
|
+
* body too, since a mixin factory's class is always nested one level inside it. Recognizes all of:
|
|
14024
|
+
* `const Foo = (Base) => class extends Base {}` (concise arrow body), `const Foo = (Base) => {
|
|
14025
|
+
* return class extends Base {}; }` (block body, `return` of a class expression), and `const Foo =
|
|
14026
|
+
* (Base) => { class FooClass extends Base {} return FooClass; }` (block body, named declaration).
|
|
13892
14027
|
* @param node the node to search from
|
|
13893
|
-
* @param name if given, only matches a class
|
|
13894
|
-
* @returns the found class
|
|
14028
|
+
* @param name if given, only matches a class with this name
|
|
14029
|
+
* @returns the found class, or `undefined`
|
|
13895
14030
|
*/
|
|
13896
14031
|
function findClassWalk(node, name) {
|
|
13897
14032
|
if (!node) return void 0;
|
|
13898
|
-
if (ts$1.isClassDeclaration(node)) {
|
|
14033
|
+
if (ts$1.isClassDeclaration(node) || ts$1.isClassExpression(node)) {
|
|
13899
14034
|
if (!name || node.name?.text === name) return node;
|
|
13900
14035
|
} else if (ts$1.isVariableDeclaration(node) && (!name || name === node.name?.text) && node.initializer && ts$1.isArrowFunction(node.initializer)) {
|
|
14036
|
+
const body = node.initializer.body;
|
|
14037
|
+
if (ts$1.isClassExpression(body)) return body;
|
|
13901
14038
|
let found;
|
|
13902
|
-
ts$1.forEachChild(
|
|
14039
|
+
ts$1.forEachChild(body, (child) => {
|
|
13903
14040
|
if (found) return;
|
|
13904
14041
|
if (ts$1.isClassDeclaration(child)) found = child;
|
|
14042
|
+
else if (ts$1.isReturnStatement(child) && child.expression && ts$1.isClassExpression(child.expression)) found = child.expression;
|
|
13905
14043
|
});
|
|
13906
14044
|
return found;
|
|
13907
14045
|
}
|
|
@@ -13914,6 +14052,22 @@ function findClassWalk(node, name) {
|
|
|
13914
14052
|
return found;
|
|
13915
14053
|
}
|
|
13916
14054
|
/**
|
|
14055
|
+
* The declared name of a mixin-factory statement (`function Foo() {}`, or `const Foo = ...`) -
|
|
14056
|
+
* survives the decorator-to-static-getter transform even when the factory's inner class doesn't
|
|
14057
|
+
* have a name of its own, so it can anchor a post-transform re-lookup via `findClassWalk`.
|
|
14058
|
+
* @param node the factory statement (or its variable declaration) to read a name from
|
|
14059
|
+
* @returns the declared name, or `undefined` if it can't be determined
|
|
14060
|
+
*/
|
|
14061
|
+
function factoryDeclaredName(node) {
|
|
14062
|
+
if (!node) return void 0;
|
|
14063
|
+
if (ts$1.isFunctionDeclaration(node)) return node.name?.text;
|
|
14064
|
+
if (ts$1.isVariableDeclaration(node)) return ts$1.isIdentifier(node.name) ? node.name.text : void 0;
|
|
14065
|
+
if (ts$1.isVariableStatement(node)) {
|
|
14066
|
+
const decl = node.declarationList.declarations[0];
|
|
14067
|
+
return decl && ts$1.isIdentifier(decl.name) ? decl.name.text : void 0;
|
|
14068
|
+
}
|
|
14069
|
+
}
|
|
14070
|
+
/**
|
|
13917
14071
|
* Matches a top-level class/function/variable declaration by name - the three
|
|
13918
14072
|
* shapes a mixin or base-class export can take (a plain class, a mixin
|
|
13919
14073
|
* factory function declaration, or a mixin factory assigned to a `const`).
|
|
@@ -13931,23 +14085,159 @@ function matchesNamedDeclaration(name) {
|
|
|
13931
14085
|
};
|
|
13932
14086
|
}
|
|
13933
14087
|
/**
|
|
13934
|
-
* Finds a re-export of `className` in `sourceFile
|
|
13935
|
-
* `
|
|
13936
|
-
* public `index.d.mts`, which re-exports its runtime API from `./declarations/stencil-public-runtime`
|
|
13937
|
-
* rather than declaring it directly).
|
|
14088
|
+
* Finds a re-export of `className` in `sourceFile`: `export { X } from './y'`, or the
|
|
14089
|
+
* bundler-split shape `import { X as name } from './y'; export { name };` (no `from` clause).
|
|
13938
14090
|
* @param sourceFile the (barrel) source file to scan
|
|
13939
14091
|
* @param className the exported name to look for
|
|
13940
|
-
* @returns the module specifier and
|
|
13941
|
-
*
|
|
14092
|
+
* @returns the module specifier and name to look for there (before any `as` aliasing), or
|
|
14093
|
+
* `undefined` if not found
|
|
13942
14094
|
*/
|
|
13943
14095
|
function findReExport(sourceFile, className) {
|
|
13944
14096
|
for (const stmt of sourceFile.statements) {
|
|
13945
|
-
if (!ts$1.isExportDeclaration(stmt) || !stmt.
|
|
13946
|
-
|
|
14097
|
+
if (!ts$1.isExportDeclaration(stmt) || !stmt.exportClause || !ts$1.isNamedExports(stmt.exportClause)) continue;
|
|
14098
|
+
const element = stmt.exportClause.elements.find((el) => el.name.text === className);
|
|
14099
|
+
if (!element) continue;
|
|
14100
|
+
if (stmt.moduleSpecifier && ts$1.isStringLiteral(stmt.moduleSpecifier)) return {
|
|
13947
14101
|
moduleSpecifier: stmt.moduleSpecifier.text,
|
|
13948
14102
|
localName: element.propertyName?.text ?? element.name.text
|
|
13949
14103
|
};
|
|
14104
|
+
const importOrigin = findImportOrigin(sourceFile, element.propertyName?.text ?? element.name.text);
|
|
14105
|
+
if (importOrigin) return importOrigin;
|
|
14106
|
+
}
|
|
14107
|
+
}
|
|
14108
|
+
/**
|
|
14109
|
+
* Finds `name`'s import in `sourceFile` and returns its module and origin name (before any `as`
|
|
14110
|
+
* aliasing; `'default'` for a default import).
|
|
14111
|
+
* @param sourceFile the source file to scan for a matching import
|
|
14112
|
+
* @param name the local (post-aliasing) name an import bound
|
|
14113
|
+
* @returns the import's module specifier and origin name, or `undefined` if `name` isn't imported
|
|
14114
|
+
*/
|
|
14115
|
+
function findImportOrigin(sourceFile, name) {
|
|
14116
|
+
for (const stmt of sourceFile.statements) {
|
|
14117
|
+
if (!ts$1.isImportDeclaration(stmt) || !stmt.importClause || !ts$1.isStringLiteral(stmt.moduleSpecifier)) continue;
|
|
14118
|
+
const moduleSpecifier = stmt.moduleSpecifier.text;
|
|
14119
|
+
if (stmt.importClause.name?.text === name) return {
|
|
14120
|
+
moduleSpecifier,
|
|
14121
|
+
localName: "default"
|
|
14122
|
+
};
|
|
14123
|
+
const bindings = stmt.importClause.namedBindings;
|
|
14124
|
+
if (!bindings || !ts$1.isNamedImports(bindings)) continue;
|
|
14125
|
+
const element = bindings.elements.find((el) => el.name.text === name);
|
|
14126
|
+
if (element) return {
|
|
14127
|
+
moduleSpecifier,
|
|
14128
|
+
localName: element.propertyName?.text ?? element.name.text
|
|
14129
|
+
};
|
|
14130
|
+
}
|
|
14131
|
+
}
|
|
14132
|
+
/**
|
|
14133
|
+
* Finds `name` as a top-level declaration in `sf` (see `matchesNamedDeclaration`), following a
|
|
14134
|
+
* same-file export alias if `name` is only ever the public side of a local rename
|
|
14135
|
+
* (`export { RealName as name }`, no `from` clause).
|
|
14136
|
+
* @param sf the source file to search
|
|
14137
|
+
* @param name the declaration name to look for, which may only exist as an export alias
|
|
14138
|
+
* @returns the matched statement, or `undefined`
|
|
14139
|
+
*/
|
|
14140
|
+
function findStatementByName(sf, name) {
|
|
14141
|
+
const direct = sf.statements.find(matchesNamedDeclaration(name));
|
|
14142
|
+
if (direct) return direct;
|
|
14143
|
+
for (const stmt of sf.statements) {
|
|
14144
|
+
if (!ts$1.isExportDeclaration(stmt) || stmt.moduleSpecifier || !stmt.exportClause || !ts$1.isNamedExports(stmt.exportClause)) continue;
|
|
14145
|
+
const aliasedName = stmt.exportClause.elements.find((el) => el.name.text === name)?.propertyName?.text;
|
|
14146
|
+
if (aliasedName && aliasedName !== name) return sf.statements.find(matchesNamedDeclaration(aliasedName));
|
|
14147
|
+
}
|
|
14148
|
+
}
|
|
14149
|
+
/**
|
|
14150
|
+
* Picks the runtime target out of a `package.json` `exports` condition entry - prefers `import`
|
|
14151
|
+
* over `require`/`node`/`default`, recursing into nested condition objects.
|
|
14152
|
+
* @param entry an `exports` map value: a path string, or nested conditions
|
|
14153
|
+
* @returns the resolved relative path, or `undefined` if no usable condition was found
|
|
14154
|
+
*/
|
|
14155
|
+
function pickJsCondition(entry) {
|
|
14156
|
+
if (typeof entry === "string") return entry;
|
|
14157
|
+
if (typeof entry !== "object" || entry === null) return;
|
|
14158
|
+
const conditions = entry;
|
|
14159
|
+
for (const key of [
|
|
14160
|
+
"import",
|
|
14161
|
+
"require",
|
|
14162
|
+
"node",
|
|
14163
|
+
"default"
|
|
14164
|
+
]) if (key in conditions) {
|
|
14165
|
+
const picked = pickJsCondition(conditions[key]);
|
|
14166
|
+
if (picked) return picked;
|
|
14167
|
+
}
|
|
14168
|
+
}
|
|
14169
|
+
/**
|
|
14170
|
+
* Reads and parses `filePath` as JS, with parent nodes set - unlike `tsGetSourceFile`, which
|
|
14171
|
+
* never triggers binding, so `.parent` stays unset and `node.getSourceFile()` returns `undefined`.
|
|
14172
|
+
* @param config the current Stencil validated config
|
|
14173
|
+
* @param filePath the absolute path of the JS file to read and parse
|
|
14174
|
+
* @returns the parsed source file, or `undefined` if it couldn't be read
|
|
14175
|
+
*/
|
|
14176
|
+
function readJsSourceFile(config, filePath) {
|
|
14177
|
+
try {
|
|
14178
|
+
const text = config.sys.readFileSync(filePath);
|
|
14179
|
+
if (typeof text !== "string") return;
|
|
14180
|
+
return ts$1.createSourceFile(filePath, text, ts$1.ScriptTarget.ESNext, true, ts$1.ScriptKind.JS);
|
|
14181
|
+
} catch {
|
|
14182
|
+
return;
|
|
14183
|
+
}
|
|
14184
|
+
}
|
|
14185
|
+
/**
|
|
14186
|
+
* Resolves the runtime JS entry for a bare (non-relative) module specifier by reading its
|
|
14187
|
+
* package's own `package.json` `exports` map, instead of `tsResolveModuleName` - which always
|
|
14188
|
+
* resolves to the package's `types` entry, an ambient signature with no body a mixin factory's
|
|
14189
|
+
* class can never be found inside.
|
|
14190
|
+
* @param config the current Stencil validated config
|
|
14191
|
+
* @param compilerCtx the current compiler context
|
|
14192
|
+
* @param moduleSpecifier the bare package specifier to resolve
|
|
14193
|
+
* @param containingFile the file the specifier was imported from
|
|
14194
|
+
* @returns the resolved JS source file, or `undefined` if it couldn't be resolved this way
|
|
14195
|
+
*/
|
|
14196
|
+
function resolveModuleJsEntry(config, compilerCtx, moduleSpecifier, containingFile) {
|
|
14197
|
+
if (isLocalModule(moduleSpecifier)) return;
|
|
14198
|
+
const pkgJsonPath = tsResolveModuleNamePackageJsonPath(config, compilerCtx, moduleSpecifier, containingFile);
|
|
14199
|
+
if (!pkgJsonPath) return;
|
|
14200
|
+
let pkgJson;
|
|
14201
|
+
try {
|
|
14202
|
+
pkgJson = JSON.parse(config.sys.readFileSync(pkgJsonPath));
|
|
14203
|
+
} catch {
|
|
14204
|
+
return;
|
|
14205
|
+
}
|
|
14206
|
+
const pkgName = pkgJson.name;
|
|
14207
|
+
if (!pkgName || !moduleSpecifier.startsWith(pkgName) || !pkgJson.exports) return;
|
|
14208
|
+
const subpath = moduleSpecifier === pkgName ? "." : `.${moduleSpecifier.slice(pkgName.length)}`;
|
|
14209
|
+
const exportsMap = pkgJson.exports;
|
|
14210
|
+
const conditionEntry = typeof exportsMap === "string" ? subpath === "." ? exportsMap : void 0 : exportsMap[subpath] ?? (subpath === "." ? exportsMap : void 0);
|
|
14211
|
+
const target = conditionEntry && pickJsCondition(conditionEntry);
|
|
14212
|
+
if (!target) return;
|
|
14213
|
+
return readJsSourceFile(config, normalizePath(join$1(dirname(pkgJsonPath), target)));
|
|
14214
|
+
}
|
|
14215
|
+
/**
|
|
14216
|
+
* Finds `className`'s class inside a resolved JS entry (see `resolveModuleJsEntry`), following
|
|
14217
|
+
* re-export hops the same way `resolveAndProcessExtendedClass` walks a `.d.ts`/`.ts` source.
|
|
14218
|
+
* @param config the current Stencil validated config
|
|
14219
|
+
* @param compilerCtx the current compiler context
|
|
14220
|
+
* @param source the JS source file to search
|
|
14221
|
+
* @param className the class (or mixin factory) name to look for
|
|
14222
|
+
* @param hopsRemaining re-export hops still allowed before giving up
|
|
14223
|
+
* @returns the found class and the source file it's declared in, or `undefined`
|
|
14224
|
+
*/
|
|
14225
|
+
function findClassInJsModule(config, compilerCtx, source, className, hopsRemaining = 3) {
|
|
14226
|
+
const matchedStatement = findStatementByName(source, className);
|
|
14227
|
+
if (matchedStatement) {
|
|
14228
|
+
const classNode = ts$1.isClassDeclaration(matchedStatement) ? matchedStatement : findClassWalk(matchedStatement);
|
|
14229
|
+
return classNode && {
|
|
14230
|
+
classNode,
|
|
14231
|
+
sourceFile: source
|
|
14232
|
+
};
|
|
13950
14233
|
}
|
|
14234
|
+
if (hopsRemaining <= 0) return;
|
|
14235
|
+
const reExport = findReExport(source, className);
|
|
14236
|
+
if (!reExport) return;
|
|
14237
|
+
const resolvedFileName = tsResolveModuleName(config, compilerCtx, reExport.moduleSpecifier, source.fileName)?.resolvedModule?.resolvedFileName;
|
|
14238
|
+
const nextSource = resolvedFileName && readJsSourceFile(config, resolvedFileName);
|
|
14239
|
+
if (!nextSource) return;
|
|
14240
|
+
return findClassInJsModule(config, compilerCtx, nextSource, reExport.localName, hopsRemaining - 1);
|
|
13951
14241
|
}
|
|
13952
14242
|
/**
|
|
13953
14243
|
* Filters `dedupeMembers` down to entries not already present in `staticMembers`
|
|
@@ -14035,7 +14325,7 @@ function convertDiskSourceFileDecorators(sourceFile, config, target = ts$1.Scrip
|
|
|
14035
14325
|
const program = ts$1.createProgram([sourceFile.fileName], compilerOptions, host);
|
|
14036
14326
|
const typeChecker = program.getTypeChecker();
|
|
14037
14327
|
const ownSourceFile = program.getSourceFile(sourceFile.fileName) ?? sourceFile;
|
|
14038
|
-
const result = ts$1.transform(ownSourceFile, [convertDecoratorsToStatic(config, [], typeChecker, program)]);
|
|
14328
|
+
const result = ts$1.transform(ownSourceFile, [convertDecoratorsToStatic(config, [], typeChecker, program, true)]);
|
|
14039
14329
|
const printed = ts$1.createPrinter({ removeComments: false }).printFile(result.transformed[0]);
|
|
14040
14330
|
return ts$1.createSourceFile(sourceFile.fileName, printed, target, true);
|
|
14041
14331
|
}
|
|
@@ -14055,9 +14345,11 @@ function convertDiskSourceFileDecorators(sourceFile, config, target = ts$1.Scrip
|
|
|
14055
14345
|
* @param ogModule the original module file of the class declaration
|
|
14056
14346
|
* @param targetScriptTarget the script target to convert decorators with, if needed
|
|
14057
14347
|
* @param barrelHopsRemaining re-export hops still allowed before giving up
|
|
14348
|
+
* @param originalModuleSpecifier the specifier this lookup started from, before any barrel hops
|
|
14349
|
+
* @param originalContainingFile the file `originalModuleSpecifier` was imported from
|
|
14058
14350
|
* @returns the found class declaration, or `undefined`
|
|
14059
14351
|
*/
|
|
14060
|
-
function resolveAndProcessExtendedClass(compilerCtx, buildCtx, classDeclaration, currentSource, moduleSpecifier, className, dependentClasses, typeChecker, ogModule, targetScriptTarget = ts$1.ScriptTarget.ESNext, barrelHopsRemaining = 1) {
|
|
14352
|
+
function resolveAndProcessExtendedClass(compilerCtx, buildCtx, classDeclaration, currentSource, moduleSpecifier, className, dependentClasses, typeChecker, ogModule, targetScriptTarget = ts$1.ScriptTarget.ESNext, barrelHopsRemaining = 1, originalModuleSpecifier = moduleSpecifier, originalContainingFile = currentSource.fileName) {
|
|
14061
14353
|
let keepLooking = true;
|
|
14062
14354
|
const foundFile = tsResolveModuleName(buildCtx.config, compilerCtx, moduleSpecifier, currentSource.fileName);
|
|
14063
14355
|
if (!foundFile?.resolvedModule || !className) return;
|
|
@@ -14073,10 +14365,10 @@ function resolveAndProcessExtendedClass(compilerCtx, buildCtx, classDeclaration,
|
|
|
14073
14365
|
}
|
|
14074
14366
|
foundSource = convertDiskSourceFileDecorators(foundSource, buildCtx.config, targetScriptTarget);
|
|
14075
14367
|
}
|
|
14076
|
-
const matchedStatement = foundSource
|
|
14368
|
+
const matchedStatement = findStatementByName(foundSource, className);
|
|
14077
14369
|
if (!matchedStatement) {
|
|
14078
14370
|
const reExport = barrelHopsRemaining > 0 ? findReExport(foundSource, className) : void 0;
|
|
14079
|
-
if (reExport) return resolveAndProcessExtendedClass(compilerCtx, buildCtx, classDeclaration, foundSource, reExport.moduleSpecifier, reExport.localName, dependentClasses, typeChecker, ogModule, targetScriptTarget, barrelHopsRemaining - 1);
|
|
14371
|
+
if (reExport) return resolveAndProcessExtendedClass(compilerCtx, buildCtx, classDeclaration, foundSource, reExport.moduleSpecifier, reExport.localName, dependentClasses, typeChecker, ogModule, targetScriptTarget, barrelHopsRemaining - 1, originalModuleSpecifier, originalContainingFile);
|
|
14080
14372
|
const err = buildWarn(buildCtx.diagnostics);
|
|
14081
14373
|
err.messageText = `Unable to find "${className}" in the imported module "${moduleSpecifier}".
|
|
14082
14374
|
Please import class / mixin-factory declarations directly and not via barrel files.`;
|
|
@@ -14084,16 +14376,25 @@ function resolveAndProcessExtendedClass(compilerCtx, buildCtx, classDeclaration,
|
|
|
14084
14376
|
return;
|
|
14085
14377
|
}
|
|
14086
14378
|
let foundClassDeclaration = matchedStatement ? ts$1.isClassDeclaration(matchedStatement) ? matchedStatement : void 0 : void 0;
|
|
14379
|
+
let foundClassSource = foundSource;
|
|
14087
14380
|
if (!foundClassDeclaration && matchedStatement) {
|
|
14088
14381
|
foundClassDeclaration = findClassWalk(matchedStatement);
|
|
14089
14382
|
keepLooking = false;
|
|
14383
|
+
if (!foundClassDeclaration && isDtsFile(foundSource.fileName) && !isLocalModule(originalModuleSpecifier)) {
|
|
14384
|
+
const jsSource = resolveModuleJsEntry(buildCtx.config, compilerCtx, originalModuleSpecifier, originalContainingFile);
|
|
14385
|
+
const foundInJs = jsSource && findClassInJsModule(buildCtx.config, compilerCtx, jsSource, className);
|
|
14386
|
+
if (foundInJs) {
|
|
14387
|
+
foundClassDeclaration = foundInJs.classNode;
|
|
14388
|
+
foundClassSource = foundInJs.sourceFile;
|
|
14389
|
+
}
|
|
14390
|
+
}
|
|
14090
14391
|
if (!foundClassDeclaration) warnMixinFactoryClassNotFound(buildCtx, className, classDeclaration);
|
|
14091
14392
|
}
|
|
14092
14393
|
if (foundClassDeclaration && !dependentClasses.some((dc) => dc.classNode === foundClassDeclaration)) {
|
|
14093
14394
|
dependentClasses.push({
|
|
14094
14395
|
classNode: foundClassDeclaration,
|
|
14095
|
-
sourceFile:
|
|
14096
|
-
fileName:
|
|
14396
|
+
sourceFile: foundClassSource,
|
|
14397
|
+
fileName: foundClassSource.fileName
|
|
14097
14398
|
});
|
|
14098
14399
|
if (keepLooking) buildExtendsTree(compilerCtx, foundClassDeclaration, dependentClasses, typeChecker, buildCtx, ogModule);
|
|
14099
14400
|
}
|
|
@@ -14122,6 +14423,7 @@ function buildExtendsTree(compilerCtx, classDeclaration, dependentClasses, typeC
|
|
|
14122
14423
|
const targetScriptTarget = (ogModule?.staticSourceFile)?.languageVersion ?? ts$1.ScriptTarget.ESNext;
|
|
14123
14424
|
let classIdentifiers = [];
|
|
14124
14425
|
let foundClassDeclaration;
|
|
14426
|
+
let foundClassName;
|
|
14125
14427
|
let keepLooking = true;
|
|
14126
14428
|
extendsClause.types.forEach((type) => {
|
|
14127
14429
|
if (ts$1.isExpressionWithTypeArguments(type) && ts$1.isCallExpression(type.expression) && type.expression.expression.getText() === "Mixin") classIdentifiers = type.expression.arguments.filter(ts$1.isIdentifier);
|
|
@@ -14138,9 +14440,11 @@ function buildExtendsTree(compilerCtx, classDeclaration, dependentClasses, typeC
|
|
|
14138
14440
|
declarations = [...source.statements];
|
|
14139
14441
|
}
|
|
14140
14442
|
foundClassDeclaration = declarations?.find(ts$1.isClassDeclaration);
|
|
14443
|
+
foundClassName = foundClassDeclaration?.name?.getText();
|
|
14141
14444
|
if (!foundClassDeclaration) {
|
|
14142
14445
|
const node = declarations?.[0];
|
|
14143
14446
|
foundClassDeclaration = findClassWalk(node);
|
|
14447
|
+
foundClassName = foundClassDeclaration?.name?.getText() ?? factoryDeclaredName(node);
|
|
14144
14448
|
if (!node) throw "revert to sad path";
|
|
14145
14449
|
keepLooking = false;
|
|
14146
14450
|
if (!foundClassDeclaration) warnMixinFactoryClassNotFound(buildCtx, extendee.getText(), classDeclaration);
|
|
@@ -14149,7 +14453,7 @@ function buildExtendsTree(compilerCtx, classDeclaration, dependentClasses, typeC
|
|
|
14149
14453
|
const foundModule = compilerCtx.moduleMap.get(foundClassDeclaration.getSourceFile().fileName);
|
|
14150
14454
|
if (foundModule) {
|
|
14151
14455
|
const moduleSourceFile = foundModule.staticSourceFile;
|
|
14152
|
-
const sourceClass = findClassWalk(moduleSourceFile,
|
|
14456
|
+
const sourceClass = findClassWalk(moduleSourceFile, foundClassName);
|
|
14153
14457
|
if (sourceClass) {
|
|
14154
14458
|
dependentClasses.push({
|
|
14155
14459
|
classNode: sourceClass,
|
|
@@ -14170,20 +14474,24 @@ function buildExtendsTree(compilerCtx, classDeclaration, dependentClasses, typeC
|
|
|
14170
14474
|
if (!buildCtx.config._isTesting) augmentDiagnosticWithNode(err, classDeclaration);
|
|
14171
14475
|
return;
|
|
14172
14476
|
}
|
|
14173
|
-
if (matchedStatement && ts$1.isClassDeclaration(matchedStatement))
|
|
14174
|
-
|
|
14477
|
+
if (matchedStatement && ts$1.isClassDeclaration(matchedStatement)) {
|
|
14478
|
+
foundClassDeclaration = matchedStatement;
|
|
14479
|
+
foundClassName = foundClassDeclaration.name?.getText();
|
|
14480
|
+
} else if (matchedStatement) {
|
|
14175
14481
|
foundClassDeclaration = findClassWalk(matchedStatement);
|
|
14482
|
+
foundClassName = foundClassDeclaration?.name?.getText() ?? extendee.getText();
|
|
14176
14483
|
keepLooking = false;
|
|
14177
14484
|
if (!foundClassDeclaration) warnMixinFactoryClassNotFound(buildCtx, extendee.getText(), classDeclaration);
|
|
14178
14485
|
} else {
|
|
14179
14486
|
foundClassDeclaration = findClassWalk(currentSource, extendee.getText());
|
|
14487
|
+
foundClassName = foundClassDeclaration?.name?.getText() ?? extendee.getText();
|
|
14180
14488
|
keepLooking = false;
|
|
14181
14489
|
}
|
|
14182
14490
|
if (foundClassDeclaration && !dependentClasses.some((dc) => dc.classNode === foundClassDeclaration)) {
|
|
14183
14491
|
const foundModule = compilerCtx.moduleMap.get(currentSource.fileName);
|
|
14184
14492
|
if (foundModule?.staticSourceFile) {
|
|
14185
14493
|
const transformedSource = foundModule.staticSourceFile;
|
|
14186
|
-
const transformedClass = findClassWalk(transformedSource,
|
|
14494
|
+
const transformedClass = findClassWalk(transformedSource, foundClassName);
|
|
14187
14495
|
if (transformedClass) {
|
|
14188
14496
|
dependentClasses.push({
|
|
14189
14497
|
classNode: transformedClass,
|
|
@@ -14468,7 +14776,7 @@ function convertInMemorySourceDecorators(sourceFile, config) {
|
|
|
14468
14776
|
const program = ts$1.createProgram([sourceFile.fileName], compilerOptions, compilerHost);
|
|
14469
14777
|
const typeChecker = program.getTypeChecker();
|
|
14470
14778
|
const ownSourceFile = program.getSourceFile(sourceFile.fileName) ?? sourceFile;
|
|
14471
|
-
const result = ts$1.transform(ownSourceFile, [convertDecoratorsToStatic(config, [], typeChecker, program)]);
|
|
14779
|
+
const result = ts$1.transform(ownSourceFile, [convertDecoratorsToStatic(config, [], typeChecker, program, true)]);
|
|
14472
14780
|
const printed = ts$1.createPrinter({ removeComments: false }).printFile(result.transformed[0]);
|
|
14473
14781
|
const scriptKind = sourceFile.fileName.endsWith(".tsx") || sourceFile.fileName.endsWith(".ts") ? ts$1.ScriptKind.TSX : ts$1.ScriptKind.JS;
|
|
14474
14782
|
return ts$1.createSourceFile(sourceFile.fileName, printed, sourceFile.languageVersion, true, scriptKind);
|
|
@@ -14493,19 +14801,6 @@ function getExtendsClassNames(node) {
|
|
|
14493
14801
|
if (ts$1.isCallExpression(expr) && expr.expression.getText() === "Mixin") return expr.arguments.filter(ts$1.isIdentifier).map((id) => id.text);
|
|
14494
14802
|
return [];
|
|
14495
14803
|
}
|
|
14496
|
-
function findImportSpecifier(sf, localName) {
|
|
14497
|
-
for (const stmt of sf.statements) {
|
|
14498
|
-
if (!ts$1.isImportDeclaration(stmt) || !ts$1.isStringLiteral(stmt.moduleSpecifier)) continue;
|
|
14499
|
-
const clause = stmt.importClause;
|
|
14500
|
-
if (!clause) continue;
|
|
14501
|
-
if (clause.name?.text === localName) return stmt.moduleSpecifier.text;
|
|
14502
|
-
const bindings = clause.namedBindings;
|
|
14503
|
-
if (bindings && ts$1.isNamedImports(bindings)) {
|
|
14504
|
-
for (const el of bindings.elements) if (el.name.text === localName) return stmt.moduleSpecifier.text;
|
|
14505
|
-
}
|
|
14506
|
-
}
|
|
14507
|
-
return null;
|
|
14508
|
-
}
|
|
14509
14804
|
/**
|
|
14510
14805
|
* A single-file mini-program (see `convertInMemorySourceDecorators`) can
|
|
14511
14806
|
* never load an imported module's own `SourceFile`, so a type that's
|
|
@@ -14529,7 +14824,7 @@ function reclassifyGlobalTypeReferences(members, declaringSf, declaringPath, res
|
|
|
14529
14824
|
if (!references) return;
|
|
14530
14825
|
Object.entries(references).forEach(([typeName, reference]) => {
|
|
14531
14826
|
if (reference.location !== "global") return;
|
|
14532
|
-
const specifier =
|
|
14827
|
+
const specifier = findImportOrigin(declaringSf, typeName)?.moduleSpecifier;
|
|
14533
14828
|
if (!specifier) return;
|
|
14534
14829
|
const resolved = resolveImport(specifier, declaringPath);
|
|
14535
14830
|
reference.location = "import";
|
|
@@ -14552,7 +14847,7 @@ function reclassifyGlobalTypeReferences(members, declaringSf, declaringPath, res
|
|
|
14552
14847
|
* differ from `sf`/`path` if resolved through a re-export), or `undefined`
|
|
14553
14848
|
*/
|
|
14554
14849
|
function findDeclarationOrReExport(sf, path, name, resolveImport) {
|
|
14555
|
-
const direct = sf
|
|
14850
|
+
const direct = findStatementByName(sf, name);
|
|
14556
14851
|
if (direct) return {
|
|
14557
14852
|
statement: direct,
|
|
14558
14853
|
sourceFile: sf,
|
|
@@ -14565,7 +14860,7 @@ function findDeclarationOrReExport(sf, path, name, resolveImport) {
|
|
|
14565
14860
|
const { code, path: resolvedPath } = resolved;
|
|
14566
14861
|
const isTs = resolvedPath.endsWith(".tsx") || resolvedPath.endsWith(".ts");
|
|
14567
14862
|
const reExportSf = ts$1.createSourceFile(resolvedPath, code, ts$1.ScriptTarget.ESNext, true, isTs ? ts$1.ScriptKind.TSX : ts$1.ScriptKind.JS);
|
|
14568
|
-
const statement = reExportSf
|
|
14863
|
+
const statement = findStatementByName(reExportSf, reExport.localName);
|
|
14569
14864
|
if (!statement) return void 0;
|
|
14570
14865
|
return {
|
|
14571
14866
|
statement,
|
|
@@ -14602,7 +14897,7 @@ function resolveAncestors(classNode, sf, path, resolveImport, visited, ancestors
|
|
|
14602
14897
|
let foundSf = sf;
|
|
14603
14898
|
let foundPath = path;
|
|
14604
14899
|
let keepLooking = true;
|
|
14605
|
-
const sameFileStatement = sf
|
|
14900
|
+
const sameFileStatement = findStatementByName(sf, parentName);
|
|
14606
14901
|
if (sameFileStatement) {
|
|
14607
14902
|
if (ts$1.isClassDeclaration(sameFileStatement)) foundClass = sameFileStatement;
|
|
14608
14903
|
else {
|
|
@@ -14611,7 +14906,7 @@ function resolveAncestors(classNode, sf, path, resolveImport, visited, ancestors
|
|
|
14611
14906
|
if (!foundClass) warnMixinFactoryClassNotFound(buildCtx, parentName, rootClassDeclaration);
|
|
14612
14907
|
}
|
|
14613
14908
|
} else {
|
|
14614
|
-
const specifier =
|
|
14909
|
+
const specifier = findImportOrigin(sf, parentName)?.moduleSpecifier;
|
|
14615
14910
|
if (!specifier) continue;
|
|
14616
14911
|
const resolved = resolveImport(specifier, path);
|
|
14617
14912
|
if (!resolved) continue;
|
|
@@ -14643,7 +14938,8 @@ function resolveAncestors(classNode, sf, path, resolveImport, visited, ancestors
|
|
|
14643
14938
|
ancestors.push({
|
|
14644
14939
|
classNode: foundClass,
|
|
14645
14940
|
sourceFile: foundSf,
|
|
14646
|
-
path: foundPath
|
|
14941
|
+
path: foundPath,
|
|
14942
|
+
factoryName: parentName
|
|
14647
14943
|
});
|
|
14648
14944
|
if (keepLooking) resolveAncestors(foundClass, foundSf, foundPath, resolveImport, visited, ancestors, rootClassDeclaration, buildCtx);
|
|
14649
14945
|
}
|
|
@@ -14660,8 +14956,8 @@ function resolveAncestors(classNode, sf, path, resolveImport, visited, ancestors
|
|
|
14660
14956
|
* @returns the ancestor's metadata
|
|
14661
14957
|
*/
|
|
14662
14958
|
function extractAncestorMeta(ancestor, cmpSourceFile, resolveImport, config) {
|
|
14663
|
-
const { classNode, sourceFile: parentSf, path: resolvedPath } = ancestor;
|
|
14664
|
-
const className = classNode.name?.text;
|
|
14959
|
+
const { classNode, sourceFile: parentSf, path: resolvedPath, factoryName } = ancestor;
|
|
14960
|
+
const className = classNode.name?.text ?? factoryName;
|
|
14665
14961
|
const parentStaticMembers = classNode.members.filter(isStaticGetter);
|
|
14666
14962
|
let resolvedClassNode = classNode;
|
|
14667
14963
|
let resolvedStaticMembers = parentStaticMembers;
|
|
@@ -14753,7 +15049,6 @@ function mergeExtendedClassMetaWithResolveImport(cmpNode, staticMembers, sourceF
|
|
|
14753
15049
|
//#region src/compiler/transformers/static-to-meta/class-methods.ts
|
|
14754
15050
|
const parseClassMethods = (classMethods, cmpMeta) => {
|
|
14755
15051
|
if (!classMethods?.length) return;
|
|
14756
|
-
const hasHostData = classMethods.includes("hostData");
|
|
14757
15052
|
cmpMeta.hasAttributeChangedCallbackFn = classMethods.includes("attributeChangedCallback");
|
|
14758
15053
|
cmpMeta.hasConnectedCallbackFn = classMethods.includes("connectedCallback");
|
|
14759
15054
|
cmpMeta.hasDisconnectedCallbackFn = classMethods.includes("disconnectedCallback");
|
|
@@ -14765,8 +15060,7 @@ const parseClassMethods = (classMethods, cmpMeta) => {
|
|
|
14765
15060
|
cmpMeta.hasComponentShouldUpdateFn = classMethods.includes("componentShouldUpdate");
|
|
14766
15061
|
cmpMeta.hasComponentDidUpdateFn = classMethods.includes("componentDidUpdate");
|
|
14767
15062
|
cmpMeta.hasLifecycle = cmpMeta.hasComponentWillLoadFn || cmpMeta.hasComponentDidLoadFn || cmpMeta.hasComponentWillUpdateFn || cmpMeta.hasComponentDidUpdateFn;
|
|
14768
|
-
cmpMeta.hasRenderFn = classMethods.includes("render")
|
|
14769
|
-
cmpMeta.hasVdomRender = cmpMeta.hasVdomRender || hasHostData;
|
|
15063
|
+
cmpMeta.hasRenderFn = classMethods.includes("render");
|
|
14770
15064
|
};
|
|
14771
15065
|
//#endregion
|
|
14772
15066
|
//#region src/compiler/transformers/static-to-meta/element-ref.ts
|
|
@@ -15146,13 +15440,13 @@ const parseStaticComponentMeta = (compilerCtx, typeChecker, cmpNode, moduleFile,
|
|
|
15146
15440
|
hasPatchClone: false,
|
|
15147
15441
|
hasPatchInsert: false
|
|
15148
15442
|
};
|
|
15149
|
-
|
|
15150
|
-
|
|
15443
|
+
if (!isCollectionDependency) validateComponentMembers(cmpNode, buildCtx);
|
|
15444
|
+
const visitComponentChildNode = (node) => {
|
|
15151
15445
|
if (ts$1.isCallExpression(node)) parseCallExpression(cmp, node, typeChecker);
|
|
15152
15446
|
else if (ts$1.isStringLiteral(node)) parseStringLiteral(cmp, node);
|
|
15153
|
-
node.forEachChild(
|
|
15447
|
+
node.forEachChild(visitComponentChildNode);
|
|
15154
15448
|
};
|
|
15155
|
-
visitComponentChildNode(cmpNode
|
|
15449
|
+
visitComponentChildNode(cmpNode);
|
|
15156
15450
|
parseClassMethods(classMethods, cmp);
|
|
15157
15451
|
cmp.hasModernPropertyDecls = detectModernPropDeclarations(cmpNode) || doesExtend;
|
|
15158
15452
|
cmp.htmlAttrNames = unique(cmp.htmlAttrNames);
|
|
@@ -15170,31 +15464,32 @@ const parseStaticComponentMeta = (compilerCtx, typeChecker, cmpNode, moduleFile,
|
|
|
15170
15464
|
return cmpNode;
|
|
15171
15465
|
};
|
|
15172
15466
|
/**
|
|
15173
|
-
*
|
|
15174
|
-
*
|
|
15175
|
-
* @param
|
|
15176
|
-
* @returns whether `classNode` is a Stencil component class
|
|
15467
|
+
* Warn about members a Stencil component class should not define.
|
|
15468
|
+
* @param cmpNode the component class declaration
|
|
15469
|
+
* @param buildCtx the current build context, used to surface diagnostics
|
|
15177
15470
|
*/
|
|
15178
|
-
const
|
|
15179
|
-
const
|
|
15180
|
-
|
|
15181
|
-
|
|
15182
|
-
const
|
|
15183
|
-
|
|
15184
|
-
|
|
15185
|
-
|
|
15186
|
-
|
|
15187
|
-
|
|
15188
|
-
|
|
15189
|
-
|
|
15190
|
-
|
|
15191
|
-
|
|
15192
|
-
|
|
15193
|
-
|
|
15194
|
-
|
|
15195
|
-
|
|
15196
|
-
|
|
15197
|
-
|
|
15471
|
+
const validateComponentMembers = (cmpNode, buildCtx) => {
|
|
15472
|
+
const componentName = cmpNode.name?.text ?? "";
|
|
15473
|
+
for (const member of cmpNode.members) {
|
|
15474
|
+
if (!member.name || !ts$1.isIdentifier(member.name)) continue;
|
|
15475
|
+
const memberName = member.name.text;
|
|
15476
|
+
/**
|
|
15477
|
+
* the component has a getter called "shadowRoot"
|
|
15478
|
+
*/
|
|
15479
|
+
if (ts$1.isGetAccessorDeclaration(member) && BLACKLISTED_COMPONENT_METHODS.includes(memberName)) {
|
|
15480
|
+
const err = buildWarn(buildCtx.diagnostics);
|
|
15481
|
+
err.messageText = `The component "${componentName}" has a getter called "${memberName}". This getter is reserved for use by Stencil components and should not be defined by the user.`;
|
|
15482
|
+
augmentDiagnosticWithNode(err, ts$1.getOriginalNode(member));
|
|
15483
|
+
}
|
|
15484
|
+
/**
|
|
15485
|
+
* the component defines a "componentShouldUpdate" method with more than one parameter,
|
|
15486
|
+
* which was the pre-v5 per-prop signature "(newVal, oldVal, propName)"
|
|
15487
|
+
*/
|
|
15488
|
+
if (ts$1.isMethodDeclaration(member) && memberName === "componentShouldUpdate" && member.parameters.length > 1) {
|
|
15489
|
+
const err = buildWarn(buildCtx.diagnostics);
|
|
15490
|
+
err.messageText = `The component "${componentName}" defines "componentShouldUpdate" with ${member.parameters.length} parameters. As of Stencil v5, "componentShouldUpdate" is called once per render with a single "changes" argument (a map of prop/state names to "{ newVal, oldVal }") instead of once per prop with "(newVal, oldVal, propName)". This method will still compile, but any extra parameters will be "undefined" at runtime.`;
|
|
15491
|
+
augmentDiagnosticWithNode(err, ts$1.getOriginalNode(member));
|
|
15492
|
+
}
|
|
15198
15493
|
}
|
|
15199
15494
|
};
|
|
15200
15495
|
const parseVirtualProps = (docs) => {
|
|
@@ -15665,6 +15960,7 @@ const transpileModule = (config, input, transformOpts) => {
|
|
|
15665
15960
|
...config,
|
|
15666
15961
|
logger: createNodeLogger()
|
|
15667
15962
|
};
|
|
15963
|
+
resetDeprecatedApiWarning();
|
|
15668
15964
|
const compilerCtx = new CompilerContext();
|
|
15669
15965
|
const buildCtx = new BuildContext(config, compilerCtx);
|
|
15670
15966
|
const tsCompilerOptions = { ...config.tsCompilerOptions };
|
|
@@ -15842,6 +16138,7 @@ const transpileCss = async (transformInput, results) => {
|
|
|
15842
16138
|
results.map = cssResults.map;
|
|
15843
16139
|
results.imports = cssResults.imports.map((p) => ({ path: p.importPath }));
|
|
15844
16140
|
results.diagnostics.push(...cssResults.diagnostics);
|
|
16141
|
+
if (transformInput.docs) results.styleDocs = cssResults.styleDocs;
|
|
15845
16142
|
};
|
|
15846
16143
|
const transpileCssSync = (transformInput, results) => {
|
|
15847
16144
|
const cssResults = transformCssToEsmSync(transformInput);
|
|
@@ -15849,6 +16146,7 @@ const transpileCssSync = (transformInput, results) => {
|
|
|
15849
16146
|
results.map = cssResults.map;
|
|
15850
16147
|
results.imports = cssResults.imports.map((p) => ({ path: p.importPath }));
|
|
15851
16148
|
results.diagnostics.push(...cssResults.diagnostics);
|
|
16149
|
+
if (transformInput.docs) results.styleDocs = cssResults.styleDocs;
|
|
15852
16150
|
};
|
|
15853
16151
|
const transpileJson = (results) => {
|
|
15854
16152
|
results.code = dataToEsm(JSON.parse(results.code), {
|
|
@@ -16279,7 +16577,7 @@ const FORM_ASSOCIATED_ATTRIBUTES = [
|
|
|
16279
16577
|
* @param cmp the metadata for the component that a type definition string is generated for
|
|
16280
16578
|
* @param typeImportData locally/imported/globally used type names, which may be used to prevent naming collisions
|
|
16281
16579
|
* @param areTypesInternal `true` if types being generated are for a project's internal purposes, `false` otherwise
|
|
16282
|
-
* @param signalBacking `true` if the component is using signal backing for its props; includes the signals map on the element interface
|
|
16580
|
+
* @param signalBacking `true` if the component is using signal backing for its props; includes the signals map on the element interface and unions JSX prop types with `ReadonlySignal<T>`
|
|
16283
16581
|
* @returns the generated types string alongside additional metadata
|
|
16284
16582
|
*/
|
|
16285
16583
|
const generateComponentTypes = (cmp, typeImportData, areTypesInternal, signalBacking = false) => {
|
|
@@ -16296,8 +16594,12 @@ const generateComponentTypes = (cmp, typeImportData, areTypesInternal, signalBac
|
|
|
16296
16594
|
const isDep = cmp.isCollectionDependency;
|
|
16297
16595
|
const propNames = new Set(propAttributes.map((p) => p.name));
|
|
16298
16596
|
const formAssociatedAttrs = cmp.formAssociated ? FORM_ASSOCIATED_ATTRIBUTES.filter((attr) => !propNames.has(attr.name)) : [];
|
|
16597
|
+
const jsxPropAttributes = signalBacking ? propAttributes.map((attr) => ({
|
|
16598
|
+
...attr,
|
|
16599
|
+
type: `${attr.type} | ReadonlySignal<${attr.type}>`
|
|
16600
|
+
})) : propAttributes;
|
|
16299
16601
|
const jsxAttributes = attributesToMultiLineString([
|
|
16300
|
-
...
|
|
16602
|
+
...jsxPropAttributes,
|
|
16301
16603
|
...eventAttributes,
|
|
16302
16604
|
...formAssociatedAttrs
|
|
16303
16605
|
], true, areTypesInternal);
|