@stencil/core 5.0.0-beta.11 → 5.0.0-beta.13

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.
@@ -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.11";
1847
+ const version = "5.0.0-beta.13";
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);
@@ -2633,7 +2646,8 @@ const getTranspileCssConfig = (compileOpts, importData, results) => {
2633
2646
  minify: false,
2634
2647
  autoprefixer: false,
2635
2648
  module: compileOpts.module,
2636
- styleImportData: compileOpts.styleImportData
2649
+ styleImportData: compileOpts.styleImportData,
2650
+ docs: compileOpts.docs === true
2637
2651
  };
2638
2652
  };
2639
2653
  const getTranspileConfigOpt = (value, validValues, defaultValue) => {
@@ -3814,6 +3828,7 @@ const validateConfig = (userConfig = {}, bootstrapConfig) => {
3814
3828
  devMode,
3815
3829
  compat: config.compat || {},
3816
3830
  generateExportMaps: isBoolean$1(config.generateExportMaps) ? config.generateExportMaps : !isString$1(config.configPath),
3831
+ enableCssOnlyComponents: config.enableCssOnlyComponents ?? true,
3817
3832
  hydratedFlag: validateHydrated(config),
3818
3833
  logLevel,
3819
3834
  logger,
@@ -3871,29 +3886,137 @@ const validateConfig = (userConfig = {}, bootstrapConfig) => {
3871
3886
  };
3872
3887
  };
3873
3888
  //#endregion
3889
+ //#region src/compiler/docs/css-doc-comments.ts
3890
+ /**
3891
+ * A JSDoc-style comment is `/** ... *\/` or `/*! ... *\/` - postcss's `Comment.text` already
3892
+ * strips the `/*`/`*\/` delimiters, so this checks the leading `*` or `!` that remains.
3893
+ * @param commentText a postcss `Comment` node's `.text`
3894
+ * @returns `true` if the comment is JSDoc-style
3895
+ */
3896
+ const isJsDocComment = (commentText) => commentText.startsWith("*") || commentText.startsWith("!");
3897
+ /**
3898
+ * Strip the leading `*`/`!` marker and per-line `*` prefixes, returning trimmed lines.
3899
+ * @param commentText a postcss `Comment` node's `.text`
3900
+ * @returns the comment's lines, delimiters and leading `*`s stripped
3901
+ */
3902
+ const normalizeJsDocLines = (commentText) => {
3903
+ return commentText.replace(/^[*!]/, "").split(/\r?\n/).map((line) => line.trim().replace(/^\*/, "").trim()).filter((line, i, arr) => !(line === "" && (i === 0 || i === arr.length - 1)));
3904
+ };
3905
+ /**
3906
+ * @param commentText a postcss `Comment` node's `.text`
3907
+ * @returns the comment's lines joined into a single trimmed line of text
3908
+ */
3909
+ const normalizeJsDocText = (commentText) => normalizeJsDocLines(commentText).filter(Boolean).join(" ").trim();
3910
+ /**
3911
+ * @param value a CSS descriptor value, possibly quoted (e.g. a `syntax` descriptor's `"<length>"`)
3912
+ * @returns the value with any surrounding quotes removed
3913
+ */
3914
+ const stripQuotes = (value) => value.replace(/^['"]|['"]$/g, "");
3915
+ /**
3916
+ * Extract the `syntax`/`initial-value` descriptors from a native `@property` at-rule's body.
3917
+ * @param node a postcss `AtRule` node whose `name` is `property`
3918
+ * @returns the extracted descriptors, if present
3919
+ */
3920
+ const parsePropertyAtRuleDescriptors = (node) => {
3921
+ let syntax;
3922
+ let initialValue;
3923
+ node.each((child) => {
3924
+ if (child.type !== "decl") return;
3925
+ if (child.prop === "syntax") syntax = stripQuotes(child.value);
3926
+ else if (child.prop === "initial-value") initialValue = child.value;
3927
+ });
3928
+ return {
3929
+ syntax,
3930
+ initialValue
3931
+ };
3932
+ };
3933
+ //#endregion
3874
3934
  //#region src/compiler/docs/style-docs.ts
3875
3935
  /**
3876
3936
  * Parse CSS docstrings that Stencil supports, as documented here:
3877
3937
  * https://stenciljs.com/docs/docs-json#css-variables
3878
3938
  *
3879
- * Docstrings found in the supplied style text will be added to the
3880
- * `styleDocs` param
3939
+ * Docstrings found in the supplied style text will be added to the `styleDocs` param, via three
3940
+ * mechanisms, in order of precedence (an earlier mechanism always wins over a later one for the
3941
+ * same custom property name):
3942
+ * 1. An explicit `@prop`/`@cssprop`/`@cssproperty` annotation in a block comment, anywhere in the file.
3943
+ * 2. A doc-commented `--foo: value;` declaration inside the component's "root" rule - `:host`
3944
+ * for `shadow`/`scoped` encapsulation, or the component's own tag selector for `none`.
3945
+ * 3. A native `@property --foo { ... }` at-rule, with the `syntax`/`initial-value` descriptors
3946
+ * surfaced as `syntax`/`default`. Not scoped - `@property` is a global registration.
3881
3947
  *
3882
3948
  * @param styleDocs the array to hold formatted CSS docstrings
3883
3949
  * @param styleText the CSS text we're working with
3884
3950
  * @param mode a mode associated with the parsed style, if applicable (e.g. this is not applicable for global styles)
3951
+ * @param tag the component's tag name, used to scope auto-detection for `none` encapsulation
3952
+ * @param encapsulation the component's encapsulation, used to pick the "root" scope for auto-detection
3885
3953
  */
3886
- function parseStyleDocs(styleDocs, styleText, mode) {
3954
+ function parseStyleDocs(styleDocs, styleText, mode, tag, encapsulation) {
3887
3955
  if (typeof styleText !== "string") return;
3888
- let match = styleText.match(CSS_DOC_START);
3956
+ let remainingText = styleText;
3957
+ let match = remainingText.match(CSS_DOC_START);
3889
3958
  while (match !== null) {
3890
- styleText = styleText.substring(match.index + match[0].length);
3891
- const endIndex = styleText.indexOf(CSS_DOC_END);
3959
+ remainingText = remainingText.substring(match.index + match[0].length);
3960
+ const endIndex = remainingText.indexOf(CSS_DOC_END);
3892
3961
  if (endIndex === -1) break;
3893
- parseCssComment(styleDocs, styleText.substring(0, endIndex), mode);
3894
- styleText = styleText.substring(endIndex + CSS_DOC_END.length);
3895
- match = styleText.match(CSS_DOC_START);
3962
+ parseCssComment(styleDocs, remainingText.substring(0, endIndex), mode);
3963
+ remainingText = remainingText.substring(endIndex + CSS_DOC_END.length);
3964
+ match = remainingText.match(CSS_DOC_START);
3965
+ }
3966
+ if (styleText.includes("--")) parseScopedAndNativeStyleDocs(styleDocs, styleText, mode, tag, encapsulation);
3967
+ }
3968
+ /**
3969
+ * Auto-detect doc-commented custom-property declarations within the component's "root" rule,
3970
+ * and native `@property` at-rule registrations, merging them into `styleDocs`. Mirrors the
3971
+ * equivalent detection for CSS-only components in `css-components/parse-css-component.ts`.
3972
+ *
3973
+ * @param styleDocs the array to hold formatted CSS docstrings - also consulted so an explicit
3974
+ * `@prop`/`@cssprop` annotation always wins over an auto-detected entry for the same name
3975
+ * @param styleText the CSS text we're working with
3976
+ * @param mode a mode associated with the parsed style, if applicable
3977
+ * @param tag the component's tag name
3978
+ * @param encapsulation the component's encapsulation
3979
+ */
3980
+ function parseScopedAndNativeStyleDocs(styleDocs, styleText, mode, tag, encapsulation) {
3981
+ let root;
3982
+ try {
3983
+ root = postcss().process(styleText, { from: void 0 }).root;
3984
+ } catch {
3985
+ return;
3896
3986
  }
3987
+ const hasName = (name) => styleDocs.some((styleDoc) => styleDoc.name === name);
3988
+ const rootSelector = encapsulation === "shadow" || encapsulation === "scoped" ? ":host" : tag;
3989
+ if (rootSelector) root.walkRules((rule) => {
3990
+ if (!rule.selectors.some((selector) => selector.trim() === rootSelector)) return;
3991
+ const children = rule.nodes;
3992
+ for (let i = 0; i < children.length; i++) {
3993
+ const child = children[i];
3994
+ if (child.type !== "decl" || !child.prop.startsWith("--") || hasName(child.prop)) continue;
3995
+ const prev = children[i - 1];
3996
+ if (!prev || prev.type !== "comment" || !isJsDocComment(prev.text)) continue;
3997
+ styleDocs.push({
3998
+ name: child.prop,
3999
+ docs: normalizeJsDocText(prev.text),
4000
+ annotation: "prop",
4001
+ mode
4002
+ });
4003
+ }
4004
+ });
4005
+ root.walkAtRules("property", (node) => {
4006
+ const propName = node.params.trim();
4007
+ if (!propName.startsWith("--") || hasName(propName)) return;
4008
+ const { syntax, initialValue } = parsePropertyAtRuleDescriptors(node);
4009
+ const prev = node.prev();
4010
+ const docs = prev && prev.type === "comment" && isJsDocComment(prev.text) ? normalizeJsDocText(prev.text) : "";
4011
+ styleDocs.push({
4012
+ name: propName,
4013
+ docs,
4014
+ annotation: "prop",
4015
+ mode,
4016
+ ...syntax && { syntax },
4017
+ ...initialValue && { default: initialValue }
4018
+ });
4019
+ });
3897
4020
  }
3898
4021
  /**
3899
4022
  * Parse a CSS comment string and insert it into the provided array of
@@ -3910,7 +4033,7 @@ function parseCssComment(styleDocs, comment, mode) {
3910
4033
  return line;
3911
4034
  }).join(" ").replace(/\t/g, " ").trim();
3912
4035
  while (comment.includes(" ")) comment = comment.replace(" ", " ");
3913
- comment.split(CSS_PROP_ANNOTATION).forEach((d) => {
4036
+ comment.split(CSS_PROP_ANNOTATION_RE).forEach((d) => {
3914
4037
  const cssDocument = d.trim();
3915
4038
  if (!cssDocument.startsWith(`--`)) return;
3916
4039
  const splt = cssDocument.split(`:`);
@@ -3933,9 +4056,11 @@ const CSS_DOC_START = /\/\*(\*|!)/;
3933
4056
  */
3934
4057
  const CSS_DOC_END = "*/";
3935
4058
  /**
3936
- * The `@prop` annotation we support within CSS docstrings
4059
+ * The `@prop` annotation we support within CSS docstrings. `@cssprop`/`@cssproperty` are also
4060
+ * recognized as synonyms (the more common names in other web-component-analyzer/docgen
4061
+ * ecosystems).
3937
4062
  */
3938
- const CSS_PROP_ANNOTATION = "@prop";
4063
+ const CSS_PROP_ANNOTATION_RE = /@(?:cssproperty|cssprop|prop)\b/;
3939
4064
  //#endregion
3940
4065
  //#region src/compiler/sys/browser-stubs/browserslist.ts
3941
4066
  const browserslist = (_query) => [];
@@ -4047,6 +4172,39 @@ const DEFAULT_BROWSER_TARGETS = [
4047
4172
  //#endregion
4048
4173
  //#region src/compiler/style/css-parser/parse-css.ts
4049
4174
  /**
4175
+ * Splits a selector list on top-level commas in a single pass: a comma only
4176
+ * splits when it's outside a quoted string and at paren depth 0, so commas in
4177
+ * `:is(:a,:has(:b))` or `[title="a,b"]` aren't treated as separators.
4178
+ * Backslash escapes (inside or outside a string) are skipped so they can't shift state,
4179
+ * so e.g. `[title="a\,b"]` is treated as a single selector.
4180
+ * @param selectors The selector list to split
4181
+ * @returns An array of selectors, with whitespace trimmed from each selector
4182
+ */
4183
+ const splitSelectorList = (selectors) => {
4184
+ const parts = [];
4185
+ let depth = 0;
4186
+ let quote = null;
4187
+ let start = 0;
4188
+ for (let i = 0; i < selectors.length; i++) {
4189
+ const ch = selectors[i];
4190
+ if (quote) {
4191
+ if (ch === "\\") i++;
4192
+ else if (ch === quote) quote = null;
4193
+ continue;
4194
+ }
4195
+ if (ch === "\"" || ch === "'") quote = ch;
4196
+ else if (ch === "\\") i++;
4197
+ else if (ch === "(") depth++;
4198
+ else if (ch === ")") depth = Math.max(0, depth - 1);
4199
+ else if (ch === "," && depth === 0) {
4200
+ parts.push(selectors.slice(start, i));
4201
+ start = i + 1;
4202
+ }
4203
+ }
4204
+ parts.push(selectors.slice(start));
4205
+ return parts.map((s) => s.trim());
4206
+ };
4207
+ /**
4050
4208
  * Parses CSS string input into an AST representation.
4051
4209
  * Used for minification, finding & resolving URLs and during SSR / prerendering, removing unused selectors.
4052
4210
  *
@@ -4171,11 +4329,8 @@ const parseCss = (css, filePath) => {
4171
4329
  const selector = () => {
4172
4330
  const m = match(/^([^{]+)/);
4173
4331
  if (!m) return null;
4174
- return trim(m[0]).replace(/\/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*\/+/g, "").replace(/"(?:\\"|[^"])*"|'(?:\\'|[^'])*'/g, function(str) {
4175
- return str.replace(/,/g, "‌");
4176
- }).split(/\s*(?![^(]*\)),\s*/).map(function(s) {
4177
- return s.replace(/\u200C/g, ",");
4178
- });
4332
+ const cleaned = trim(m[0]).replace(/\/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*\/+/g, "");
4333
+ return splitSelectorList(cleaned);
4179
4334
  };
4180
4335
  const declaration = () => {
4181
4336
  const pos = position();
@@ -9187,9 +9342,9 @@ const serializeSymbol = (checker, symbol) => {
9187
9342
  };
9188
9343
  };
9189
9344
  /**
9190
- * Maps a TypeScript 4.3+ JSDocTagInfo to a flattened Stencil CompilerJsDocTagInfo.
9345
+ * Maps a TypeScript 4.3+ JSDocTagInfo to a flattened Stencil JsonDocsTag.
9191
9346
  * @param tags A readonly array of JSDocTagInfo objects.
9192
- * @returns An array of CompilerJsDocTagInfo objects.
9347
+ * @returns An array of JsonDocsTag objects.
9193
9348
  */
9194
9349
  const mapJSDocTagInfo = (tags) => {
9195
9350
  return tags.map((tag) => ({
@@ -9600,7 +9755,7 @@ const transformCssToEsmModule = (input) => {
9600
9755
  defaultVarName: createCssVarName(input.file, input.mode),
9601
9756
  styleDocs: []
9602
9757
  };
9603
- if (input.docs) parseStyleDocs(results.styleDocs, input.input, input.mode);
9758
+ if (input.docs) parseStyleDocs(results.styleDocs, input.input, input.mode, input.tag, input.encapsulation);
9604
9759
  try {
9605
9760
  const varNames = /* @__PURE__ */ new Set([results.defaultVarName]);
9606
9761
  if (isString$1(input.tag) && input.encapsulation === "scoped") {
@@ -9697,7 +9852,7 @@ const getCssToEsmImports = (varNames, cssText, filePath, modeName) => {
9697
9852
  isNodeModule: false
9698
9853
  };
9699
9854
  if (!isLocalCssImport(cssImportData.srcImportText)) continue;
9700
- else if (cssImportData.url === "stencil-globals" || cssImportData.url === "stencil-hydrate") continue;
9855
+ else if (cssImportData.url === "stencil-component-globals" || cssImportData.url === "stencil-hydrate" || cssImportData.url === "stencil-css-components") continue;
9701
9856
  else if (isCssNodeModule(cssImportData.url)) {
9702
9857
  cssImportData.filePath = cssImportData.url.substring(1);
9703
9858
  cssImportData.isNodeModule = true;
@@ -9912,6 +10067,7 @@ var BuildContext = class {
9912
10067
  completedTasks = [];
9913
10068
  compilerCtx;
9914
10069
  components = [];
10070
+ cssOnlyComponents = [];
9915
10071
  componentGraph = /* @__PURE__ */ new Map();
9916
10072
  config;
9917
10073
  data = {};
@@ -9953,6 +10109,7 @@ var BuildContext = class {
9953
10109
  styleBuildCount = 0;
9954
10110
  stylesPromise = null;
9955
10111
  stylesUpdated = [];
10112
+ globalStylesUpdated = [];
9956
10113
  timeSpan = null;
9957
10114
  timestamp;
9958
10115
  transpileBuildCount = 0;
@@ -10133,12 +10290,18 @@ var CompilerContext = class {
10133
10290
  */
10134
10291
  prevStylesMap = /* @__PURE__ */ new Map();
10135
10292
  globalStyleCache = /* @__PURE__ */ new Map();
10293
+ cssOnlyComponentsCache = /* @__PURE__ */ new Map();
10294
+ stencilCss;
10295
+ globalStyleVirtualImports = /* @__PURE__ */ new Map();
10136
10296
  styleModeNames = /* @__PURE__ */ new Set();
10137
10297
  worker = null;
10138
10298
  reset() {
10139
10299
  this.cache.clear();
10140
10300
  this.cssModuleImports.clear();
10141
10301
  this.globalStyleCache.clear();
10302
+ this.cssOnlyComponentsCache.clear();
10303
+ this.stencilCss = void 0;
10304
+ this.globalStyleVirtualImports.clear();
10142
10305
  this.collections.length = 0;
10143
10306
  this.compilerOptions = null;
10144
10307
  this.hasSuccessfulBuild = false;
@@ -13956,23 +14119,159 @@ function matchesNamedDeclaration(name) {
13956
14119
  };
13957
14120
  }
13958
14121
  /**
13959
- * Finds a re-export of `className` in `sourceFile` - `export { X } from './y'` or
13960
- * `export { X as Y } from './y'` - as used by barrel entry points (e.g. `@stencil/core`'s own
13961
- * public `index.d.mts`, which re-exports its runtime API from `./declarations/stencil-public-runtime`
13962
- * rather than declaring it directly).
14122
+ * Finds a re-export of `className` in `sourceFile`: `export { X } from './y'`, or the
14123
+ * bundler-split shape `import { X as name } from './y'; export { name };` (no `from` clause).
13963
14124
  * @param sourceFile the (barrel) source file to scan
13964
14125
  * @param className the exported name to look for
13965
- * @returns the module specifier and the name to look for in that module (the local name, before
13966
- * any `as` aliasing), or `undefined` if no matching re-export is found
14126
+ * @returns the module specifier and name to look for there (before any `as` aliasing), or
14127
+ * `undefined` if not found
13967
14128
  */
13968
14129
  function findReExport(sourceFile, className) {
13969
14130
  for (const stmt of sourceFile.statements) {
13970
- if (!ts$1.isExportDeclaration(stmt) || !stmt.moduleSpecifier || !ts$1.isStringLiteral(stmt.moduleSpecifier) || !stmt.exportClause || !ts$1.isNamedExports(stmt.exportClause)) continue;
13971
- for (const element of stmt.exportClause.elements) if (element.name.text === className) return {
14131
+ if (!ts$1.isExportDeclaration(stmt) || !stmt.exportClause || !ts$1.isNamedExports(stmt.exportClause)) continue;
14132
+ const element = stmt.exportClause.elements.find((el) => el.name.text === className);
14133
+ if (!element) continue;
14134
+ if (stmt.moduleSpecifier && ts$1.isStringLiteral(stmt.moduleSpecifier)) return {
13972
14135
  moduleSpecifier: stmt.moduleSpecifier.text,
13973
14136
  localName: element.propertyName?.text ?? element.name.text
13974
14137
  };
14138
+ const importOrigin = findImportOrigin(sourceFile, element.propertyName?.text ?? element.name.text);
14139
+ if (importOrigin) return importOrigin;
14140
+ }
14141
+ }
14142
+ /**
14143
+ * Finds `name`'s import in `sourceFile` and returns its module and origin name (before any `as`
14144
+ * aliasing; `'default'` for a default import).
14145
+ * @param sourceFile the source file to scan for a matching import
14146
+ * @param name the local (post-aliasing) name an import bound
14147
+ * @returns the import's module specifier and origin name, or `undefined` if `name` isn't imported
14148
+ */
14149
+ function findImportOrigin(sourceFile, name) {
14150
+ for (const stmt of sourceFile.statements) {
14151
+ if (!ts$1.isImportDeclaration(stmt) || !stmt.importClause || !ts$1.isStringLiteral(stmt.moduleSpecifier)) continue;
14152
+ const moduleSpecifier = stmt.moduleSpecifier.text;
14153
+ if (stmt.importClause.name?.text === name) return {
14154
+ moduleSpecifier,
14155
+ localName: "default"
14156
+ };
14157
+ const bindings = stmt.importClause.namedBindings;
14158
+ if (!bindings || !ts$1.isNamedImports(bindings)) continue;
14159
+ const element = bindings.elements.find((el) => el.name.text === name);
14160
+ if (element) return {
14161
+ moduleSpecifier,
14162
+ localName: element.propertyName?.text ?? element.name.text
14163
+ };
14164
+ }
14165
+ }
14166
+ /**
14167
+ * Finds `name` as a top-level declaration in `sf` (see `matchesNamedDeclaration`), following a
14168
+ * same-file export alias if `name` is only ever the public side of a local rename
14169
+ * (`export { RealName as name }`, no `from` clause).
14170
+ * @param sf the source file to search
14171
+ * @param name the declaration name to look for, which may only exist as an export alias
14172
+ * @returns the matched statement, or `undefined`
14173
+ */
14174
+ function findStatementByName(sf, name) {
14175
+ const direct = sf.statements.find(matchesNamedDeclaration(name));
14176
+ if (direct) return direct;
14177
+ for (const stmt of sf.statements) {
14178
+ if (!ts$1.isExportDeclaration(stmt) || stmt.moduleSpecifier || !stmt.exportClause || !ts$1.isNamedExports(stmt.exportClause)) continue;
14179
+ const aliasedName = stmt.exportClause.elements.find((el) => el.name.text === name)?.propertyName?.text;
14180
+ if (aliasedName && aliasedName !== name) return sf.statements.find(matchesNamedDeclaration(aliasedName));
14181
+ }
14182
+ }
14183
+ /**
14184
+ * Picks the runtime target out of a `package.json` `exports` condition entry - prefers `import`
14185
+ * over `require`/`node`/`default`, recursing into nested condition objects.
14186
+ * @param entry an `exports` map value: a path string, or nested conditions
14187
+ * @returns the resolved relative path, or `undefined` if no usable condition was found
14188
+ */
14189
+ function pickJsCondition(entry) {
14190
+ if (typeof entry === "string") return entry;
14191
+ if (typeof entry !== "object" || entry === null) return;
14192
+ const conditions = entry;
14193
+ for (const key of [
14194
+ "import",
14195
+ "require",
14196
+ "node",
14197
+ "default"
14198
+ ]) if (key in conditions) {
14199
+ const picked = pickJsCondition(conditions[key]);
14200
+ if (picked) return picked;
14201
+ }
14202
+ }
14203
+ /**
14204
+ * Reads and parses `filePath` as JS, with parent nodes set - unlike `tsGetSourceFile`, which
14205
+ * never triggers binding, so `.parent` stays unset and `node.getSourceFile()` returns `undefined`.
14206
+ * @param config the current Stencil validated config
14207
+ * @param filePath the absolute path of the JS file to read and parse
14208
+ * @returns the parsed source file, or `undefined` if it couldn't be read
14209
+ */
14210
+ function readJsSourceFile(config, filePath) {
14211
+ try {
14212
+ const text = config.sys.readFileSync(filePath);
14213
+ if (typeof text !== "string") return;
14214
+ return ts$1.createSourceFile(filePath, text, ts$1.ScriptTarget.ESNext, true, ts$1.ScriptKind.JS);
14215
+ } catch {
14216
+ return;
14217
+ }
14218
+ }
14219
+ /**
14220
+ * Resolves the runtime JS entry for a bare (non-relative) module specifier by reading its
14221
+ * package's own `package.json` `exports` map, instead of `tsResolveModuleName` - which always
14222
+ * resolves to the package's `types` entry, an ambient signature with no body a mixin factory's
14223
+ * class can never be found inside.
14224
+ * @param config the current Stencil validated config
14225
+ * @param compilerCtx the current compiler context
14226
+ * @param moduleSpecifier the bare package specifier to resolve
14227
+ * @param containingFile the file the specifier was imported from
14228
+ * @returns the resolved JS source file, or `undefined` if it couldn't be resolved this way
14229
+ */
14230
+ function resolveModuleJsEntry(config, compilerCtx, moduleSpecifier, containingFile) {
14231
+ if (isLocalModule(moduleSpecifier)) return;
14232
+ const pkgJsonPath = tsResolveModuleNamePackageJsonPath(config, compilerCtx, moduleSpecifier, containingFile);
14233
+ if (!pkgJsonPath) return;
14234
+ let pkgJson;
14235
+ try {
14236
+ pkgJson = JSON.parse(config.sys.readFileSync(pkgJsonPath));
14237
+ } catch {
14238
+ return;
14239
+ }
14240
+ const pkgName = pkgJson.name;
14241
+ if (!pkgName || !moduleSpecifier.startsWith(pkgName) || !pkgJson.exports) return;
14242
+ const subpath = moduleSpecifier === pkgName ? "." : `.${moduleSpecifier.slice(pkgName.length)}`;
14243
+ const exportsMap = pkgJson.exports;
14244
+ const conditionEntry = typeof exportsMap === "string" ? subpath === "." ? exportsMap : void 0 : exportsMap[subpath] ?? (subpath === "." ? exportsMap : void 0);
14245
+ const target = conditionEntry && pickJsCondition(conditionEntry);
14246
+ if (!target) return;
14247
+ return readJsSourceFile(config, normalizePath(join$1(dirname(pkgJsonPath), target)));
14248
+ }
14249
+ /**
14250
+ * Finds `className`'s class inside a resolved JS entry (see `resolveModuleJsEntry`), following
14251
+ * re-export hops the same way `resolveAndProcessExtendedClass` walks a `.d.ts`/`.ts` source.
14252
+ * @param config the current Stencil validated config
14253
+ * @param compilerCtx the current compiler context
14254
+ * @param source the JS source file to search
14255
+ * @param className the class (or mixin factory) name to look for
14256
+ * @param hopsRemaining re-export hops still allowed before giving up
14257
+ * @returns the found class and the source file it's declared in, or `undefined`
14258
+ */
14259
+ function findClassInJsModule(config, compilerCtx, source, className, hopsRemaining = 3) {
14260
+ const matchedStatement = findStatementByName(source, className);
14261
+ if (matchedStatement) {
14262
+ const classNode = ts$1.isClassDeclaration(matchedStatement) ? matchedStatement : findClassWalk(matchedStatement);
14263
+ return classNode && {
14264
+ classNode,
14265
+ sourceFile: source
14266
+ };
13975
14267
  }
14268
+ if (hopsRemaining <= 0) return;
14269
+ const reExport = findReExport(source, className);
14270
+ if (!reExport) return;
14271
+ const resolvedFileName = tsResolveModuleName(config, compilerCtx, reExport.moduleSpecifier, source.fileName)?.resolvedModule?.resolvedFileName;
14272
+ const nextSource = resolvedFileName && readJsSourceFile(config, resolvedFileName);
14273
+ if (!nextSource) return;
14274
+ return findClassInJsModule(config, compilerCtx, nextSource, reExport.localName, hopsRemaining - 1);
13976
14275
  }
13977
14276
  /**
13978
14277
  * Filters `dedupeMembers` down to entries not already present in `staticMembers`
@@ -14080,9 +14379,11 @@ function convertDiskSourceFileDecorators(sourceFile, config, target = ts$1.Scrip
14080
14379
  * @param ogModule the original module file of the class declaration
14081
14380
  * @param targetScriptTarget the script target to convert decorators with, if needed
14082
14381
  * @param barrelHopsRemaining re-export hops still allowed before giving up
14382
+ * @param originalModuleSpecifier the specifier this lookup started from, before any barrel hops
14383
+ * @param originalContainingFile the file `originalModuleSpecifier` was imported from
14083
14384
  * @returns the found class declaration, or `undefined`
14084
14385
  */
14085
- function resolveAndProcessExtendedClass(compilerCtx, buildCtx, classDeclaration, currentSource, moduleSpecifier, className, dependentClasses, typeChecker, ogModule, targetScriptTarget = ts$1.ScriptTarget.ESNext, barrelHopsRemaining = 1) {
14386
+ function resolveAndProcessExtendedClass(compilerCtx, buildCtx, classDeclaration, currentSource, moduleSpecifier, className, dependentClasses, typeChecker, ogModule, targetScriptTarget = ts$1.ScriptTarget.ESNext, barrelHopsRemaining = 1, originalModuleSpecifier = moduleSpecifier, originalContainingFile = currentSource.fileName) {
14086
14387
  let keepLooking = true;
14087
14388
  const foundFile = tsResolveModuleName(buildCtx.config, compilerCtx, moduleSpecifier, currentSource.fileName);
14088
14389
  if (!foundFile?.resolvedModule || !className) return;
@@ -14098,10 +14399,10 @@ function resolveAndProcessExtendedClass(compilerCtx, buildCtx, classDeclaration,
14098
14399
  }
14099
14400
  foundSource = convertDiskSourceFileDecorators(foundSource, buildCtx.config, targetScriptTarget);
14100
14401
  }
14101
- const matchedStatement = foundSource.statements.find(matchesNamedDeclaration(className));
14402
+ const matchedStatement = findStatementByName(foundSource, className);
14102
14403
  if (!matchedStatement) {
14103
14404
  const reExport = barrelHopsRemaining > 0 ? findReExport(foundSource, className) : void 0;
14104
- if (reExport) return resolveAndProcessExtendedClass(compilerCtx, buildCtx, classDeclaration, foundSource, reExport.moduleSpecifier, reExport.localName, dependentClasses, typeChecker, ogModule, targetScriptTarget, barrelHopsRemaining - 1);
14405
+ if (reExport) return resolveAndProcessExtendedClass(compilerCtx, buildCtx, classDeclaration, foundSource, reExport.moduleSpecifier, reExport.localName, dependentClasses, typeChecker, ogModule, targetScriptTarget, barrelHopsRemaining - 1, originalModuleSpecifier, originalContainingFile);
14105
14406
  const err = buildWarn(buildCtx.diagnostics);
14106
14407
  err.messageText = `Unable to find "${className}" in the imported module "${moduleSpecifier}".
14107
14408
  Please import class / mixin-factory declarations directly and not via barrel files.`;
@@ -14109,16 +14410,25 @@ function resolveAndProcessExtendedClass(compilerCtx, buildCtx, classDeclaration,
14109
14410
  return;
14110
14411
  }
14111
14412
  let foundClassDeclaration = matchedStatement ? ts$1.isClassDeclaration(matchedStatement) ? matchedStatement : void 0 : void 0;
14413
+ let foundClassSource = foundSource;
14112
14414
  if (!foundClassDeclaration && matchedStatement) {
14113
14415
  foundClassDeclaration = findClassWalk(matchedStatement);
14114
14416
  keepLooking = false;
14417
+ if (!foundClassDeclaration && isDtsFile(foundSource.fileName) && !isLocalModule(originalModuleSpecifier)) {
14418
+ const jsSource = resolveModuleJsEntry(buildCtx.config, compilerCtx, originalModuleSpecifier, originalContainingFile);
14419
+ const foundInJs = jsSource && findClassInJsModule(buildCtx.config, compilerCtx, jsSource, className);
14420
+ if (foundInJs) {
14421
+ foundClassDeclaration = foundInJs.classNode;
14422
+ foundClassSource = foundInJs.sourceFile;
14423
+ }
14424
+ }
14115
14425
  if (!foundClassDeclaration) warnMixinFactoryClassNotFound(buildCtx, className, classDeclaration);
14116
14426
  }
14117
14427
  if (foundClassDeclaration && !dependentClasses.some((dc) => dc.classNode === foundClassDeclaration)) {
14118
14428
  dependentClasses.push({
14119
14429
  classNode: foundClassDeclaration,
14120
- sourceFile: foundSource,
14121
- fileName: foundFile.resolvedModule.resolvedFileName
14430
+ sourceFile: foundClassSource,
14431
+ fileName: foundClassSource.fileName
14122
14432
  });
14123
14433
  if (keepLooking) buildExtendsTree(compilerCtx, foundClassDeclaration, dependentClasses, typeChecker, buildCtx, ogModule);
14124
14434
  }
@@ -14525,19 +14835,6 @@ function getExtendsClassNames(node) {
14525
14835
  if (ts$1.isCallExpression(expr) && expr.expression.getText() === "Mixin") return expr.arguments.filter(ts$1.isIdentifier).map((id) => id.text);
14526
14836
  return [];
14527
14837
  }
14528
- function findImportSpecifier(sf, localName) {
14529
- for (const stmt of sf.statements) {
14530
- if (!ts$1.isImportDeclaration(stmt) || !ts$1.isStringLiteral(stmt.moduleSpecifier)) continue;
14531
- const clause = stmt.importClause;
14532
- if (!clause) continue;
14533
- if (clause.name?.text === localName) return stmt.moduleSpecifier.text;
14534
- const bindings = clause.namedBindings;
14535
- if (bindings && ts$1.isNamedImports(bindings)) {
14536
- for (const el of bindings.elements) if (el.name.text === localName) return stmt.moduleSpecifier.text;
14537
- }
14538
- }
14539
- return null;
14540
- }
14541
14838
  /**
14542
14839
  * A single-file mini-program (see `convertInMemorySourceDecorators`) can
14543
14840
  * never load an imported module's own `SourceFile`, so a type that's
@@ -14561,7 +14858,7 @@ function reclassifyGlobalTypeReferences(members, declaringSf, declaringPath, res
14561
14858
  if (!references) return;
14562
14859
  Object.entries(references).forEach(([typeName, reference]) => {
14563
14860
  if (reference.location !== "global") return;
14564
- const specifier = findImportSpecifier(declaringSf, typeName);
14861
+ const specifier = findImportOrigin(declaringSf, typeName)?.moduleSpecifier;
14565
14862
  if (!specifier) return;
14566
14863
  const resolved = resolveImport(specifier, declaringPath);
14567
14864
  reference.location = "import";
@@ -14584,7 +14881,7 @@ function reclassifyGlobalTypeReferences(members, declaringSf, declaringPath, res
14584
14881
  * differ from `sf`/`path` if resolved through a re-export), or `undefined`
14585
14882
  */
14586
14883
  function findDeclarationOrReExport(sf, path, name, resolveImport) {
14587
- const direct = sf.statements.find(matchesNamedDeclaration(name));
14884
+ const direct = findStatementByName(sf, name);
14588
14885
  if (direct) return {
14589
14886
  statement: direct,
14590
14887
  sourceFile: sf,
@@ -14597,7 +14894,7 @@ function findDeclarationOrReExport(sf, path, name, resolveImport) {
14597
14894
  const { code, path: resolvedPath } = resolved;
14598
14895
  const isTs = resolvedPath.endsWith(".tsx") || resolvedPath.endsWith(".ts");
14599
14896
  const reExportSf = ts$1.createSourceFile(resolvedPath, code, ts$1.ScriptTarget.ESNext, true, isTs ? ts$1.ScriptKind.TSX : ts$1.ScriptKind.JS);
14600
- const statement = reExportSf.statements.find(matchesNamedDeclaration(reExport.localName));
14897
+ const statement = findStatementByName(reExportSf, reExport.localName);
14601
14898
  if (!statement) return void 0;
14602
14899
  return {
14603
14900
  statement,
@@ -14634,7 +14931,7 @@ function resolveAncestors(classNode, sf, path, resolveImport, visited, ancestors
14634
14931
  let foundSf = sf;
14635
14932
  let foundPath = path;
14636
14933
  let keepLooking = true;
14637
- const sameFileStatement = sf.statements.find(matchesNamedDeclaration(parentName));
14934
+ const sameFileStatement = findStatementByName(sf, parentName);
14638
14935
  if (sameFileStatement) {
14639
14936
  if (ts$1.isClassDeclaration(sameFileStatement)) foundClass = sameFileStatement;
14640
14937
  else {
@@ -14643,7 +14940,7 @@ function resolveAncestors(classNode, sf, path, resolveImport, visited, ancestors
14643
14940
  if (!foundClass) warnMixinFactoryClassNotFound(buildCtx, parentName, rootClassDeclaration);
14644
14941
  }
14645
14942
  } else {
14646
- const specifier = findImportSpecifier(sf, parentName);
14943
+ const specifier = findImportOrigin(sf, parentName)?.moduleSpecifier;
14647
14944
  if (!specifier) continue;
14648
14945
  const resolved = resolveImport(specifier, path);
14649
14946
  if (!resolved) continue;
@@ -15875,6 +16172,7 @@ const transpileCss = async (transformInput, results) => {
15875
16172
  results.map = cssResults.map;
15876
16173
  results.imports = cssResults.imports.map((p) => ({ path: p.importPath }));
15877
16174
  results.diagnostics.push(...cssResults.diagnostics);
16175
+ if (transformInput.docs) results.styleDocs = cssResults.styleDocs;
15878
16176
  };
15879
16177
  const transpileCssSync = (transformInput, results) => {
15880
16178
  const cssResults = transformCssToEsmSync(transformInput);
@@ -15882,6 +16180,7 @@ const transpileCssSync = (transformInput, results) => {
15882
16180
  results.map = cssResults.map;
15883
16181
  results.imports = cssResults.imports.map((p) => ({ path: p.importPath }));
15884
16182
  results.diagnostics.push(...cssResults.diagnostics);
16183
+ if (transformInput.docs) results.styleDocs = cssResults.styleDocs;
15885
16184
  };
15886
16185
  const transpileJson = (results) => {
15887
16186
  results.code = dataToEsm(JSON.parse(results.code), {