@stencil/core 5.0.0-beta.1 → 5.0.0-beta.10

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.
@@ -1,9 +1,9 @@
1
1
  import { n as __require } from "./rolldown-runtime-BhDjJH2R.mjs";
2
- import { S as HYDRATED_CSS } from "./client-BRu0GtRn.mjs";
2
+ import { S as HYDRATED_CSS } from "./client-Db2rdxUM.mjs";
3
3
  import { $ as VALID_CONFIG_OUTPUT_TARGETS, A as DIST_LAZY, D as COPY, E as COLLECTION_MANIFEST_FILE_NAME, J as STANDALONE, L as EVENT_FLAGS, P as DOCS_JSON, Q as TYPES, R as GENERATED_DTS, S as ASSETS, T as COLLECTION_APP_DATA_FILE_NAME, U as LOADER_BUNDLE, X as STYLE_EXT, Y as STATS, a as isBoolean$1, b as unique, c as isFunction, d as isObject, et as WATCH_FLAGS, f as isString$1, g as sortBy, i as fromEntries, l as isIterable, m as noop, p as mergeIntoWith, r as flatOne, t as dashToPascalCase, u as isNumber$1, v as toDashCase, w as COLLECTION, y as toTitleCase, z as GLOBAL_STYLE } from "./helpers-Cpp3qc3u.mjs";
4
4
  import { E as escapeRegExpSpecialCharacters, S as formatComponentRuntimeMeta, T as byteSize, _ as loadRolldownDiagnostics, a as getSourceMappingUrlForEndOfFile, b as splitLineBreaks, c as err, f as augmentDiagnosticWithNode, g as isRolldownError, h as createOnWarnFn, i as getInlineSourceMappingUrlLinker, l as map, m as loadTypeScriptDiagnostics, n as validateComponentTag, p as loadTypeScriptDiagnostic, r as isRemoteUrl, s as rolldownToStencilSourceMap, t as validateComponentModes, u as ok, w as stringifyRuntimeData, x as isRootPath, y as normalizeDiagnostics } from "./validation-DAdGTrys.mjs";
5
5
  import { $ as catchError, A as isOutputTargetDocsVscode, B as relativeImport, C as isOutputTargetDistLazy, D as isOutputTargetDocsCustomElementsManifest, E as isOutputTargetDocsCustom, F as isOutputTargetStandalone, G as normalizeFsPathQuery, H as join$2, I as isOutputTargetStats, J as resolve$2, K as normalizePath, L as isOutputTargetTypes, M as isOutputTargetLoaderBundle, N as isOutputTargetSsr, O as isOutputTargetDocsJson, P as isOutputTargetSsrWasm, Q as buildWarn, R as isOutputTargetWww, S as isOutputTargetCustom, T as isOutputTargetDocsAgentSkill, W as normalizeFsPath, X as buildError, Z as buildJsonFileError, a as hasDependency, b as isOutputTargetCollection, d as parsePackageJson, et as hasError, f as readOnlyArrayHasStringMember, g as getComponentsDtsSrcFilePath, h as filterExcludedComponents, i as getTextDocs, j as isOutputTargetGlobalStyle, k as isOutputTargetDocsReadme, l as isTsFile, m as filterActiveTargets, n as createJsVarName, o as isDtsFile, p as readPackageJson, q as relative$1, r as generatePreamble, rt as isGlob, t as addDocBlock, tt as hasWarning, u as isTsxFile, v as getComponentsFromModules, w as isOutputTargetDocs, x as isOutputTargetCopy, y as isOutputTargetAssets, z as isValidConfigOutputTarget } from "./util-IKfWWLJo.mjs";
6
- import { i as CustomElementsExportBehaviorOptions, o as buildEvents, r as createNodeLogger, t as createNodeSys } from "./node-75gQKkFz.mjs";
6
+ import { i as CustomElementsExportBehaviorOptions, o as buildEvents, r as createNodeLogger, t as createNodeSys } from "./node-BKtLn2R9.mjs";
7
7
  import ts, { default as ts$1 } from "typescript";
8
8
  import { readFileSync } from "node:fs";
9
9
  import { basename, dirname, isAbsolute, join, resolve } from "node:path";
@@ -342,12 +342,13 @@ const scopeSelector = (selector, scopeSelectorText, hostSelector, slotSelector)
342
342
  else return shallowPart.trim();
343
343
  }).join(", ");
344
344
  };
345
+ const isScopableAtRule = (selector) => selector.startsWith("@media") || selector.startsWith("@supports") || selector.startsWith("@page") || selector.startsWith("@document") || selector.startsWith("@layer");
345
346
  const scopeSelectors = (cssText, scopeSelectorText, hostSelector, slotSelector, commentOriginalSelector) => {
346
347
  return processRules(cssText, (rule) => {
347
348
  let selector = rule.selector;
348
349
  let content = rule.content;
349
350
  if (rule.selector[0] !== "@") selector = scopeSelector(rule.selector, scopeSelectorText, hostSelector, slotSelector);
350
- else if (rule.selector.startsWith("@media") || rule.selector.startsWith("@supports") || rule.selector.startsWith("@page") || rule.selector.startsWith("@document")) content = scopeSelectors(rule.content, scopeSelectorText, hostSelector, slotSelector, commentOriginalSelector);
351
+ else if (isScopableAtRule(rule.selector)) content = scopeSelectors(rule.content, scopeSelectorText, hostSelector, slotSelector, commentOriginalSelector);
351
352
  return {
352
353
  selector: selector.replace(/\s{2,}/g, " ").trim(),
353
354
  content
@@ -439,14 +440,12 @@ const scopeCss = (cssText, scopeId, commentOriginalSelector) => {
439
440
  rule.selector = placeholder + rule.selector;
440
441
  return rule;
441
442
  };
442
- cssText = processRules(cssText, (rule) => {
443
+ const commentSelectors = (input) => processRules(input, (rule) => {
443
444
  if (rule.selector[0] !== "@") return processCommentedSelector(rule);
444
- else if (rule.selector.startsWith("@media") || rule.selector.startsWith("@supports") || rule.selector.startsWith("@page") || rule.selector.startsWith("@document")) {
445
- rule.content = processRules(rule.content, processCommentedSelector);
446
- return rule;
447
- }
445
+ if (isScopableAtRule(rule.selector)) rule.content = commentSelectors(rule.content);
448
446
  return rule;
449
447
  });
448
+ cssText = commentSelectors(cssText);
450
449
  }
451
450
  const scoped = scopeCssText(cssText, scopeId, hostScopeId, slotScopeId, commentOriginalSelector);
452
451
  cssText = [scoped.cssText, ...commentsWithHash].join("\n");
@@ -462,9 +461,9 @@ const scopeCss = (cssText, scopeId, commentOriginalSelector) => {
462
461
  };
463
462
  //#endregion
464
463
  //#region src/version.ts
465
- const version = "5.0.0-beta.1";
466
- const buildId = "1788442714";
467
- const vermoji = "🚓";
464
+ const version = "5.0.0-beta.10";
465
+ const buildId = "1789054824";
466
+ const vermoji = "🚘";
468
467
  /**
469
468
  * Get the installed version of a tool/dependency.
470
469
  * Handles packages with exports maps that point to subdirectories.
@@ -7373,7 +7372,13 @@ function createEntryModule(cmps) {
7373
7372
  }
7374
7373
  const getLazyEntry = (isBrowser, assetPath, externalRuntime = false) => {
7375
7374
  const s = new MagicString(``);
7376
- s.append(`export { setNonce, setRegistry, setTagTransformer } from '${STENCIL_CORE_ID}';\n`);
7375
+ const platformExports = [
7376
+ "setNonce",
7377
+ "setRegistry",
7378
+ "setTagTransformer"
7379
+ ];
7380
+ if (assetPath) platformExports.push("setAssetPath");
7381
+ s.append(`export { ${platformExports.join(", ")} } from '${STENCIL_CORE_ID}';\n`);
7377
7382
  s.append(`import { bootstrapLazy } from '${STENCIL_CORE_ID}';\n`);
7378
7383
  if (isBrowser) {
7379
7384
  if (assetPath) s.append(`import { setAssetPath } from '${STENCIL_CORE_ID}';\n`);
@@ -9388,15 +9393,17 @@ const generateStandaloneTypesOutput = async (config, compilerCtx, buildCtx, type
9388
9393
  if (outputTarget.autoLoader) {
9389
9394
  const loaderFileName = typeof outputTarget.autoLoader === "object" ? outputTarget.autoLoader.fileName || "loader" : "loader";
9390
9395
  const loaderDtsPath = join$2(outputTarget.dir, `${loaderFileName}.d.ts`);
9391
- const loaderDtsCode = generateLoaderType();
9396
+ const hasAssets = buildCtx.components.some((cmp) => cmp.assetsDirs != null && cmp.assetsDirs.length > 0);
9397
+ const loaderDtsCode = generateLoaderType(hasAssets);
9392
9398
  await compilerCtx.fs.writeFile(loaderDtsPath, loaderDtsCode, { outputTargetType: outputTarget.type });
9393
9399
  }
9394
9400
  };
9395
9401
  /**
9396
9402
  * Generate a type declaration file for the auto-loader module
9403
+ * @param hasAssets whether any component declares assets, matching whether setAssetPath is actually exported
9397
9404
  * @returns the contents of the type declaration file for the loader
9398
9405
  */
9399
- const generateLoaderType = () => {
9406
+ const generateLoaderType = (hasAssets) => {
9400
9407
  return [
9401
9408
  `/**`,
9402
9409
  ` * Start the auto-loader, scanning the DOM and watching for changes.`,
@@ -9410,7 +9417,18 @@ const generateLoaderType = () => {
9410
9417
  ` * Stop the auto-loader and disconnect the MutationObserver.`,
9411
9418
  ` */`,
9412
9419
  `export declare function stop(): void;`,
9413
- ``
9420
+ ``,
9421
+ ...hasAssets ? [
9422
+ `/**`,
9423
+ ` * Used to manually set the base path where component assets can be found. The`,
9424
+ ` * default is relative to this module's own location, which may not hold once a`,
9425
+ ` * consuming bundler relocates it - call this before start() to override.`,
9426
+ ` * @param path the asset path to set`,
9427
+ ` * @returns the set path`,
9428
+ ` */`,
9429
+ `export declare function setAssetPath(path: string): string;`,
9430
+ ``
9431
+ ] : []
9414
9432
  ].join("\n");
9415
9433
  };
9416
9434
  /**
@@ -9900,7 +9918,7 @@ const FORM_ASSOCIATED_ATTRIBUTES = [
9900
9918
  * @param cmp the metadata for the component that a type definition string is generated for
9901
9919
  * @param typeImportData locally/imported/globally used type names, which may be used to prevent naming collisions
9902
9920
  * @param areTypesInternal `true` if types being generated are for a project's internal purposes, `false` otherwise
9903
- * @param signalBacking `true` if the component is using signal backing for its props; includes the signals map on the element interface
9921
+ * @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>`
9904
9922
  * @returns the generated types string alongside additional metadata
9905
9923
  */
9906
9924
  const generateComponentTypes = (cmp, typeImportData, areTypesInternal, signalBacking = false) => {
@@ -9917,8 +9935,12 @@ const generateComponentTypes = (cmp, typeImportData, areTypesInternal, signalBac
9917
9935
  const isDep = cmp.isCollectionDependency;
9918
9936
  const propNames = new Set(propAttributes.map((p) => p.name));
9919
9937
  const formAssociatedAttrs = cmp.formAssociated ? FORM_ASSOCIATED_ATTRIBUTES.filter((attr) => !propNames.has(attr.name)) : [];
9938
+ const jsxPropAttributes = signalBacking ? propAttributes.map((attr) => ({
9939
+ ...attr,
9940
+ type: `${attr.type} | ReadonlySignal<${attr.type}>`
9941
+ })) : propAttributes;
9920
9942
  const jsxAttributes = attributesToMultiLineString([
9921
- ...propAttributes,
9943
+ ...jsxPropAttributes,
9922
9944
  ...eventAttributes,
9923
9945
  ...formAssociatedAttrs
9924
9946
  ], true, areTypesInternal);
@@ -10355,8 +10377,9 @@ const generateTypesOutput = async (config, compilerCtx, buildCtx, outputTarget)
10355
10377
  await generateAppTypes(config, compilerCtx, buildCtx, distPath);
10356
10378
  const typesDir = outputTarget.dir;
10357
10379
  if (distDtsFilePath) await generateStandaloneTypes(config, compilerCtx, buildCtx, typesDir);
10358
- if (config.outputTargets.some(isOutputTargetLoaderBundle)) await generateLoaderTypes(compilerCtx, typesDir);
10359
- if (config.outputTargets.some(isOutputTargetStandalone)) await generateStandaloneApiTypes(compilerCtx, typesDir);
10380
+ const hasAssets = buildCtx.components.some((cmp) => cmp.assetsDirs != null && cmp.assetsDirs.length > 0);
10381
+ if (config.outputTargets.some(isOutputTargetLoaderBundle)) await generateLoaderTypes(compilerCtx, typesDir, hasAssets);
10382
+ if (config.outputTargets.some(isOutputTargetStandalone)) await generateStandaloneApiTypes(compilerCtx, typesDir, hasAssets);
10360
10383
  };
10361
10384
  /**
10362
10385
  * Generate loader.d.ts file for the loader entry point.
@@ -10364,8 +10387,9 @@ const generateTypesOutput = async (config, compilerCtx, buildCtx, outputTarget)
10364
10387
  *
10365
10388
  * @param compilerCtx the current compiler context
10366
10389
  * @param typesDir the directory to write the loader.d.ts file to
10390
+ * @param hasAssets whether any component declares assets, matching whether setAssetPath is actually exported
10367
10391
  */
10368
- const generateLoaderTypes = async (compilerCtx, typesDir) => {
10392
+ const generateLoaderTypes = async (compilerCtx, typesDir, hasAssets) => {
10369
10393
  const loaderDtsContent = `export * from './components';
10370
10394
  export interface CustomElementsDefineOptions {
10371
10395
  exclude?: string[];
@@ -10401,7 +10425,17 @@ export type TagTransformer = (tag: string) => string;
10401
10425
  * Useful for namespacing (e.g. \`my-button\` → \`acme-button\`) in multi-library pages.
10402
10426
  */
10403
10427
  export declare function setTagTransformer(transformer: TagTransformer): void;
10404
- `;
10428
+ ${hasAssets ? `
10429
+ /**
10430
+ * Used to manually set the base path where component assets can be found. The
10431
+ * default is relative to this module's own location, which may not hold once a
10432
+ * bundler relocates it, or once its CDN URL diverges from the assets' own -
10433
+ * import and call this before the module's default has been used to override it.
10434
+ * @param path the asset path to set
10435
+ * @returns the set path
10436
+ */
10437
+ export declare function setAssetPath(path: string): string;
10438
+ ` : ""}`;
10405
10439
  const loaderDtsPath = join$2(typesDir, "loader.d.ts");
10406
10440
  await compilerCtx.fs.writeFile(loaderDtsPath, loaderDtsContent);
10407
10441
  };
@@ -10411,8 +10445,9 @@ export declare function setTagTransformer(transformer: TagTransformer): void;
10411
10445
  *
10412
10446
  * @param compilerCtx the current compiler context
10413
10447
  * @param typesDir the directory to write the standalone.d.ts file to
10448
+ * @param hasAssets whether any component declares assets, matching whether setAssetPath is actually exported
10414
10449
  */
10415
- const generateStandaloneApiTypes = async (compilerCtx, typesDir) => {
10450
+ const generateStandaloneApiTypes = async (compilerCtx, typesDir, hasAssets) => {
10416
10451
  const standaloneDtsContent = `/**
10417
10452
  * Used to specify a nonce value that corresponds with an application's CSP.
10418
10453
  * When set, the nonce will be added to all dynamically created script and style tags at runtime.
@@ -10445,7 +10480,16 @@ export interface SetPlatformOptions {
10445
10480
  rel?: (el: EventTarget, eventName: string, listener: EventListenerOrEventListenerObject, options: boolean | AddEventListenerOptions) => void;
10446
10481
  }
10447
10482
  export declare const setPlatformOptions: (opts: SetPlatformOptions) => void;
10448
- `;
10483
+ ${hasAssets ? `
10484
+ /**
10485
+ * Used to manually set the base path where component assets can be found. The
10486
+ * default is relative to this module's own location, which may not hold once a
10487
+ * consuming bundler relocates it - call this before any component module is imported to override.
10488
+ * @param path the asset path to set
10489
+ * @returns the set path
10490
+ */
10491
+ export declare const setAssetPath: (path: string) => string;
10492
+ ` : ""}`;
10449
10493
  const standaloneDtsPath = join$2(typesDir, "standalone.d.ts");
10450
10494
  await compilerCtx.fs.writeFile(standaloneDtsPath, standaloneDtsContent);
10451
10495
  };
@@ -12406,7 +12450,7 @@ const generateLoaderModule = (components, outputTarget, relativeAssetPath) => {
12406
12450
  const autoStart = typeof autoLoaderConfig === "object" ? autoLoaderConfig.autoStart !== false : true;
12407
12451
  const tagsList = components.map((cmp) => `'${cmp.tagName}'`).join(", ");
12408
12452
  const switchCases = components.map((cmp) => ` case '${cmp.tagName}': module = await import('./${cmp.tagName}.js'); break;`).join("\n");
12409
- const assetPathImport = relativeAssetPath ? `import { setAssetPath } from '${STENCIL_INTERNAL_STANDALONE_CLIENT_PLATFORM_ID}';\n` : "";
12453
+ const assetPathImport = relativeAssetPath ? `import { setAssetPath } from '${STENCIL_INTERNAL_STANDALONE_CLIENT_PLATFORM_ID}';\nexport { setAssetPath };\n` : "";
12410
12454
  const assetPathInit = relativeAssetPath ? `setAssetPath(new URL('${relativeAssetPath}', String(import.meta.url)).href);\n` : "";
12411
12455
  return `
12412
12456
  import { transformTag, getRegistry } from '${STENCIL_INTERNAL_STANDALONE_CLIENT_PLATFORM_ID}';
@@ -12767,12 +12811,25 @@ const addStandaloneInputs = (config, buildCtx, bundleOpts, outputTarget) => {
12767
12811
  const indexImports = [];
12768
12812
  const indexExports = [];
12769
12813
  const exportNames = [];
12814
+ let relativeAssetPath;
12815
+ if (components.some((cmp) => cmp.assetsDirs != null && cmp.assetsDirs.length > 0)) {
12816
+ const assetsTarget = config.outputTargets.find(isOutputTargetAssets);
12817
+ if (assetsTarget?.dir && outputTarget.dir) {
12818
+ const rel = relative$1(outputTarget.dir, assetsTarget.dir);
12819
+ relativeAssetPath = rel.endsWith("/") ? rel : rel + "/";
12820
+ }
12821
+ }
12770
12822
  components.forEach((cmp) => {
12771
12823
  const exp = [];
12772
12824
  const exportName = dashToPascalCase(cmp.tagName);
12773
12825
  const importName = cmp.componentClassName;
12774
12826
  const importAs = `$Cmp${exportName}`;
12775
12827
  const coreKey = `\0${exportName}`;
12828
+ if (relativeAssetPath) {
12829
+ exp.push(`import { setAssetPath } from '${STENCIL_INTERNAL_STANDALONE_CLIENT_PLATFORM_ID}';`);
12830
+ exp.push(`export { setAssetPath };`);
12831
+ exp.push(`setAssetPath(new URL('${relativeAssetPath}', import.meta.url).href);`);
12832
+ }
12776
12833
  if (cmp.isPlain) {
12777
12834
  exp.push(`export { ${importName} as ${exportName} } from '${cmp.sourceFilePath}';`);
12778
12835
  indexExports.push(`export { ${exportName} } from '${coreKey}';`);
@@ -12787,14 +12844,6 @@ const addStandaloneInputs = (config, buildCtx, bundleOpts, outputTarget) => {
12787
12844
  bundleOpts.inputs[cmp.tagName] = coreKey;
12788
12845
  bundleOpts.loader[coreKey] = exp.join("\n");
12789
12846
  });
12790
- let relativeAssetPath;
12791
- if (components.some((cmp) => cmp.assetsDirs != null && cmp.assetsDirs.length > 0)) {
12792
- const assetsTarget = config.outputTargets.find(isOutputTargetAssets);
12793
- if (assetsTarget?.dir && outputTarget.dir) {
12794
- const rel = relative$1(outputTarget.dir, assetsTarget.dir);
12795
- relativeAssetPath = rel.endsWith("/") ? rel : rel + "/";
12796
- }
12797
- }
12798
12847
  bundleOpts.loader["\0core"] = generateEntryPoint(outputTarget, indexImports, indexExports, exportNames, relativeAssetPath);
12799
12848
  if (outputTarget.autoLoader) {
12800
12849
  const loaderFileName = typeof outputTarget.autoLoader === "object" ? outputTarget.autoLoader.fileName || "loader" : "loader";
@@ -12818,6 +12867,7 @@ const generateEntryPoint = (outputTarget, cmpImports = [], cmpExports = [], cmpN
12818
12867
  exports.push(`export { setNonce, setRegistry, setTagTransformer, setPlatformOptions } from '${STENCIL_INTERNAL_STANDALONE_CLIENT_PLATFORM_ID}';`, `export * from '${USER_INDEX_ENTRY_ID}';`);
12819
12868
  if (relativeAssetPath) {
12820
12869
  imports.push(`import { setAssetPath } from '${STENCIL_INTERNAL_STANDALONE_CLIENT_PLATFORM_ID}';`);
12870
+ exports.push(`export { setAssetPath };`);
12821
12871
  body.push(`setAssetPath(new URL('${relativeAssetPath}', import.meta.url).href);`);
12822
12872
  }
12823
12873
  if (outputTarget.includeGlobalScripts !== false) {
@@ -15225,14 +15275,16 @@ const parseWatchDecorator = (typeChecker, method, decoratorName) => {
15225
15275
  * @param diagnostics for surfacing errors and warnings
15226
15276
  * @param typeChecker a TypeScript typechecker instance
15227
15277
  * @param program a {@link ts.Program} object
15278
+ * @param includeClassExpressions also convert `ts.ClassExpression` nodes (e.g. a mixin factory's
15279
+ * `(Base) => class extends Base {}`), not just named `ts.ClassDeclaration`s
15228
15280
  * @returns a TypeScript transformer factory which can be passed to
15229
15281
  * TypeScript to transform source code during the compilation process
15230
15282
  */
15231
- const convertDecoratorsToStatic = (config, diagnostics, typeChecker, program) => {
15283
+ const convertDecoratorsToStatic = (config, diagnostics, typeChecker, program, includeClassExpressions = false) => {
15232
15284
  return (transformCtx) => {
15233
15285
  let sourceFile;
15234
15286
  const visit = (node) => {
15235
- if (ts.isClassDeclaration(node)) return visitClassDeclaration(config, diagnostics, typeChecker, program, node, sourceFile);
15287
+ if (ts.isClassDeclaration(node) || includeClassExpressions && ts.isClassExpression(node)) return visitClassDeclaration(config, diagnostics, typeChecker, program, node, sourceFile);
15236
15288
  return ts.visitEachChild(node, visit, transformCtx);
15237
15289
  };
15238
15290
  return (tsSourceFile) => {
@@ -15242,10 +15294,10 @@ const convertDecoratorsToStatic = (config, diagnostics, typeChecker, program) =>
15242
15294
  };
15243
15295
  };
15244
15296
  /**
15245
- * Visit {@link ts.ClassDeclaration} nodes as part of the tree-traversal
15297
+ * Visit {@link ts.ClassDeclaration}/{@link ts.ClassExpression} nodes as part of the tree-traversal
15246
15298
  * required for {@link convertDecoratorsToStatic} to work.
15247
15299
  *
15248
- * If a given class declaration node is not decorated with `@Component` this
15300
+ * If a given class node is not decorated with `@Component` this
15249
15301
  * function will simply return it as-is. If it _is_ decorated, then it will
15250
15302
  *
15251
15303
  * 1. Convert the arguments to the `@Component` decorator to static values.
@@ -15254,7 +15306,7 @@ const convertDecoratorsToStatic = (config, diagnostics, typeChecker, program) =>
15254
15306
  * to static values.
15255
15307
  * 3. Validate that the various options selected by the component author are
15256
15308
  * valid, both individually and in combination.
15257
- * 4. Return an updated class declaration node which is ready to be used in
15309
+ * 4. Return an updated class node which is ready to be used in
15258
15310
  * the rest of the Stencil compilation pipeline (e.g. in output target
15259
15311
  * generation).
15260
15312
  *
@@ -15273,7 +15325,7 @@ const visitClassDeclaration = (config, diagnostics, typeChecker, program, classN
15273
15325
  const decoratedMembers = classMembers.filter((member) => (retrieveTsDecorators(member)?.length ?? 0) > 0);
15274
15326
  if (!decoratedMembers.length && !componentDecorator) return classNode;
15275
15327
  const filteredMethodsAndFields = removeStencilMethodDecorators(Array.from(classMembers), diagnostics, importAliasMap);
15276
- if (componentDecorator) componentDecoratorToStatic(config, typeChecker, diagnostics, classNode, filteredMethodsAndFields, componentDecorator);
15328
+ if (componentDecorator && ts.isClassDeclaration(classNode)) componentDecoratorToStatic(config, typeChecker, diagnostics, classNode, filteredMethodsAndFields, componentDecorator);
15277
15329
  if (decoratedMembers.length > 0) {
15278
15330
  const serializers = serializeDecoratorsToStatic(typeChecker, decoratedMembers, filteredMethodsAndFields, importAliasMap.get("PropSerialize"), "PropSerialize", importAliasMap.get("Prop"));
15279
15331
  const deserializers = serializeDecoratorsToStatic(typeChecker, decoratedMembers, filteredMethodsAndFields, importAliasMap.get("AttrDeserialize"), "AttrDeserialize", importAliasMap.get("Prop"));
@@ -15289,7 +15341,8 @@ const visitClassDeclaration = (config, diagnostics, typeChecker, program, classN
15289
15341
  validateMethods(diagnostics, classMembers);
15290
15342
  const currentDecorators = retrieveTsDecorators(classNode);
15291
15343
  const updatedClassFields = updateConstructor(classNode, filteredMethodsAndFields, []);
15292
- return ts.factory.updateClassDeclaration(classNode, [...filterDecorators(currentDecorators, CLASS_DECORATORS_TO_REMOVE.map((decorator) => importAliasMap.get(decorator))) ?? [], ...retrieveTsModifiers(classNode) ?? []], classNode.name, classNode.typeParameters, classNode.heritageClauses, updatedClassFields);
15344
+ const updatedModifiers = [...filterDecorators(currentDecorators, CLASS_DECORATORS_TO_REMOVE.map((decorator) => importAliasMap.get(decorator))) ?? [], ...retrieveTsModifiers(classNode) ?? []];
15345
+ return ts.isClassDeclaration(classNode) ? ts.factory.updateClassDeclaration(classNode, updatedModifiers, classNode.name, classNode.typeParameters, classNode.heritageClauses, updatedClassFields) : ts.factory.updateClassExpression(classNode, updatedModifiers, classNode.name, classNode.typeParameters, classNode.heritageClauses, updatedClassFields);
15293
15346
  };
15294
15347
  /**
15295
15348
  * Take a list of `ClassElement` AST nodes and remove any decorators from
@@ -15473,11 +15526,11 @@ const parseStaticWatchers = (staticMembers) => {
15473
15526
  //#endregion
15474
15527
  //#region src/compiler/transformers/static-to-meta/class-extension/shared.ts
15475
15528
  /**
15476
- * Warns when an `extends`/`Mixin(...)` target resolved to *something* but no class declaration
15477
- * could be found inside it - e.g. a mixin factory whose class isn't a named declaration
15478
- * (`(Base) => class extends Base {}` rather than `(Base) => { class Foo extends Base {} return
15479
- * Foo; }`). Without this, the target is silently dropped: any `@Prop`/`@State` etc. it declares
15480
- * just never appears on the extending component, with no diagnostic explaining why.
15529
+ * Warns when an `extends`/`Mixin(...)` target resolved to *something* but no class literal
15530
+ * could be found inside it - e.g. a mixin factory that returns an existing class by reference
15531
+ * (`(Base) => SomeExistingClass`) rather than a class literal. Without this, the target is
15532
+ * silently dropped: any `@Prop`/`@State` etc. it declares just never appears on the extending
15533
+ * component, with no diagnostic explaining why.
15481
15534
  * @param buildCtx used to surface the warning - omit to warn silently (e.g. from tests)
15482
15535
  * @param targetName the identifier the target was reached through
15483
15536
  * @param anchor the node to attach the warning to
@@ -15485,27 +15538,32 @@ const parseStaticWatchers = (staticMembers) => {
15485
15538
  function warnMixinFactoryClassNotFound(buildCtx, targetName, anchor) {
15486
15539
  if (!buildCtx) return;
15487
15540
  const err = buildWarn(buildCtx.diagnostics);
15488
- err.messageText = `Found "${targetName}", but couldn't find a class declaration inside it. If it's meant to be a mixin factory, make sure it declares and returns a named class, e.g. \`(Base) => { class ${targetName}Class extends Base {} return ${targetName}Class; }\` - a factory that returns a class expression directly (\`(Base) => class extends Base {}\`) isn't recognized, and any \`@Prop\`/\`@State\`/etc. it declares won't be applied.`;
15541
+ 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.`;
15489
15542
  if (!buildCtx.config._isTesting) augmentDiagnosticWithNode(err, anchor);
15490
15543
  }
15491
15544
  /**
15492
- * Walks the AST looking for a class declaration, optionally by name - descends
15493
- * into a mixin factory's wrapping function (arrow function or `function`
15494
- * declaration) body too, since a mixin factory's class is always nested one
15495
- * level inside it (e.g. `const Foo = (Base) => class extends Base {}`).
15545
+ * Walks the AST looking for a class declaration or class expression, optionally by name -
15546
+ * descends into a mixin factory's wrapping function (arrow function or `function` declaration)
15547
+ * body too, since a mixin factory's class is always nested one level inside it. Recognizes all of:
15548
+ * `const Foo = (Base) => class extends Base {}` (concise arrow body), `const Foo = (Base) => {
15549
+ * return class extends Base {}; }` (block body, `return` of a class expression), and `const Foo =
15550
+ * (Base) => { class FooClass extends Base {} return FooClass; }` (block body, named declaration).
15496
15551
  * @param node the node to search from
15497
- * @param name if given, only matches a class declaration with this name
15498
- * @returns the found class declaration, or `undefined`
15552
+ * @param name if given, only matches a class with this name
15553
+ * @returns the found class, or `undefined`
15499
15554
  */
15500
15555
  function findClassWalk(node, name) {
15501
15556
  if (!node) return void 0;
15502
- if (ts.isClassDeclaration(node)) {
15557
+ if (ts.isClassDeclaration(node) || ts.isClassExpression(node)) {
15503
15558
  if (!name || node.name?.text === name) return node;
15504
15559
  } else if (ts.isVariableDeclaration(node) && (!name || name === node.name?.text) && node.initializer && ts.isArrowFunction(node.initializer)) {
15560
+ const body = node.initializer.body;
15561
+ if (ts.isClassExpression(body)) return body;
15505
15562
  let found;
15506
- ts.forEachChild(node.initializer.body, (child) => {
15563
+ ts.forEachChild(body, (child) => {
15507
15564
  if (found) return;
15508
15565
  if (ts.isClassDeclaration(child)) found = child;
15566
+ else if (ts.isReturnStatement(child) && child.expression && ts.isClassExpression(child.expression)) found = child.expression;
15509
15567
  });
15510
15568
  return found;
15511
15569
  }
@@ -15518,6 +15576,22 @@ function findClassWalk(node, name) {
15518
15576
  return found;
15519
15577
  }
15520
15578
  /**
15579
+ * The declared name of a mixin-factory statement (`function Foo() {}`, or `const Foo = ...`) -
15580
+ * survives the decorator-to-static-getter transform even when the factory's inner class doesn't
15581
+ * have a name of its own, so it can anchor a post-transform re-lookup via `findClassWalk`.
15582
+ * @param node the factory statement (or its variable declaration) to read a name from
15583
+ * @returns the declared name, or `undefined` if it can't be determined
15584
+ */
15585
+ function factoryDeclaredName(node) {
15586
+ if (!node) return void 0;
15587
+ if (ts.isFunctionDeclaration(node)) return node.name?.text;
15588
+ if (ts.isVariableDeclaration(node)) return ts.isIdentifier(node.name) ? node.name.text : void 0;
15589
+ if (ts.isVariableStatement(node)) {
15590
+ const decl = node.declarationList.declarations[0];
15591
+ return decl && ts.isIdentifier(decl.name) ? decl.name.text : void 0;
15592
+ }
15593
+ }
15594
+ /**
15521
15595
  * Matches a top-level class/function/variable declaration by name - the three
15522
15596
  * shapes a mixin or base-class export can take (a plain class, a mixin
15523
15597
  * factory function declaration, or a mixin factory assigned to a `const`).
@@ -15639,7 +15713,7 @@ function convertDiskSourceFileDecorators(sourceFile, config, target = ts.ScriptT
15639
15713
  const program = ts.createProgram([sourceFile.fileName], compilerOptions, host);
15640
15714
  const typeChecker = program.getTypeChecker();
15641
15715
  const ownSourceFile = program.getSourceFile(sourceFile.fileName) ?? sourceFile;
15642
- const result = ts.transform(ownSourceFile, [convertDecoratorsToStatic(config, [], typeChecker, program)]);
15716
+ const result = ts.transform(ownSourceFile, [convertDecoratorsToStatic(config, [], typeChecker, program, true)]);
15643
15717
  const printed = ts.createPrinter({ removeComments: false }).printFile(result.transformed[0]);
15644
15718
  return ts.createSourceFile(sourceFile.fileName, printed, target, true);
15645
15719
  }
@@ -15726,6 +15800,7 @@ function buildExtendsTree(compilerCtx, classDeclaration, dependentClasses, typeC
15726
15800
  const targetScriptTarget = (ogModule?.staticSourceFile)?.languageVersion ?? ts.ScriptTarget.ESNext;
15727
15801
  let classIdentifiers = [];
15728
15802
  let foundClassDeclaration;
15803
+ let foundClassName;
15729
15804
  let keepLooking = true;
15730
15805
  extendsClause.types.forEach((type) => {
15731
15806
  if (ts.isExpressionWithTypeArguments(type) && ts.isCallExpression(type.expression) && type.expression.expression.getText() === "Mixin") classIdentifiers = type.expression.arguments.filter(ts.isIdentifier);
@@ -15742,9 +15817,11 @@ function buildExtendsTree(compilerCtx, classDeclaration, dependentClasses, typeC
15742
15817
  declarations = [...source.statements];
15743
15818
  }
15744
15819
  foundClassDeclaration = declarations?.find(ts.isClassDeclaration);
15820
+ foundClassName = foundClassDeclaration?.name?.getText();
15745
15821
  if (!foundClassDeclaration) {
15746
15822
  const node = declarations?.[0];
15747
15823
  foundClassDeclaration = findClassWalk(node);
15824
+ foundClassName = foundClassDeclaration?.name?.getText() ?? factoryDeclaredName(node);
15748
15825
  if (!node) throw "revert to sad path";
15749
15826
  keepLooking = false;
15750
15827
  if (!foundClassDeclaration) warnMixinFactoryClassNotFound(buildCtx, extendee.getText(), classDeclaration);
@@ -15753,7 +15830,7 @@ function buildExtendsTree(compilerCtx, classDeclaration, dependentClasses, typeC
15753
15830
  const foundModule = compilerCtx.moduleMap.get(foundClassDeclaration.getSourceFile().fileName);
15754
15831
  if (foundModule) {
15755
15832
  const moduleSourceFile = foundModule.staticSourceFile;
15756
- const sourceClass = findClassWalk(moduleSourceFile, foundClassDeclaration.name?.getText());
15833
+ const sourceClass = findClassWalk(moduleSourceFile, foundClassName);
15757
15834
  if (sourceClass) {
15758
15835
  dependentClasses.push({
15759
15836
  classNode: sourceClass,
@@ -15774,20 +15851,24 @@ function buildExtendsTree(compilerCtx, classDeclaration, dependentClasses, typeC
15774
15851
  if (!buildCtx.config._isTesting) augmentDiagnosticWithNode(err, classDeclaration);
15775
15852
  return;
15776
15853
  }
15777
- if (matchedStatement && ts.isClassDeclaration(matchedStatement)) foundClassDeclaration = matchedStatement;
15778
- else if (matchedStatement) {
15854
+ if (matchedStatement && ts.isClassDeclaration(matchedStatement)) {
15855
+ foundClassDeclaration = matchedStatement;
15856
+ foundClassName = foundClassDeclaration.name?.getText();
15857
+ } else if (matchedStatement) {
15779
15858
  foundClassDeclaration = findClassWalk(matchedStatement);
15859
+ foundClassName = foundClassDeclaration?.name?.getText() ?? extendee.getText();
15780
15860
  keepLooking = false;
15781
15861
  if (!foundClassDeclaration) warnMixinFactoryClassNotFound(buildCtx, extendee.getText(), classDeclaration);
15782
15862
  } else {
15783
15863
  foundClassDeclaration = findClassWalk(currentSource, extendee.getText());
15864
+ foundClassName = foundClassDeclaration?.name?.getText() ?? extendee.getText();
15784
15865
  keepLooking = false;
15785
15866
  }
15786
15867
  if (foundClassDeclaration && !dependentClasses.some((dc) => dc.classNode === foundClassDeclaration)) {
15787
15868
  const foundModule = compilerCtx.moduleMap.get(currentSource.fileName);
15788
15869
  if (foundModule?.staticSourceFile) {
15789
15870
  const transformedSource = foundModule.staticSourceFile;
15790
- const transformedClass = findClassWalk(transformedSource, foundClassDeclaration.name?.getText());
15871
+ const transformedClass = findClassWalk(transformedSource, foundClassName);
15791
15872
  if (transformedClass) {
15792
15873
  dependentClasses.push({
15793
15874
  classNode: transformedClass,
@@ -16072,7 +16153,7 @@ function convertInMemorySourceDecorators(sourceFile, config) {
16072
16153
  const program = ts.createProgram([sourceFile.fileName], compilerOptions, compilerHost);
16073
16154
  const typeChecker = program.getTypeChecker();
16074
16155
  const ownSourceFile = program.getSourceFile(sourceFile.fileName) ?? sourceFile;
16075
- const result = ts.transform(ownSourceFile, [convertDecoratorsToStatic(config, [], typeChecker, program)]);
16156
+ const result = ts.transform(ownSourceFile, [convertDecoratorsToStatic(config, [], typeChecker, program, true)]);
16076
16157
  const printed = ts.createPrinter({ removeComments: false }).printFile(result.transformed[0]);
16077
16158
  const scriptKind = sourceFile.fileName.endsWith(".tsx") || sourceFile.fileName.endsWith(".ts") ? ts.ScriptKind.TSX : ts.ScriptKind.JS;
16078
16159
  return ts.createSourceFile(sourceFile.fileName, printed, sourceFile.languageVersion, true, scriptKind);
@@ -16247,7 +16328,8 @@ function resolveAncestors(classNode, sf, path, resolveImport, visited, ancestors
16247
16328
  ancestors.push({
16248
16329
  classNode: foundClass,
16249
16330
  sourceFile: foundSf,
16250
- path: foundPath
16331
+ path: foundPath,
16332
+ factoryName: parentName
16251
16333
  });
16252
16334
  if (keepLooking) resolveAncestors(foundClass, foundSf, foundPath, resolveImport, visited, ancestors, rootClassDeclaration, buildCtx);
16253
16335
  }
@@ -16264,8 +16346,8 @@ function resolveAncestors(classNode, sf, path, resolveImport, visited, ancestors
16264
16346
  * @returns the ancestor's metadata
16265
16347
  */
16266
16348
  function extractAncestorMeta(ancestor, cmpSourceFile, resolveImport, config) {
16267
- const { classNode, sourceFile: parentSf, path: resolvedPath } = ancestor;
16268
- const className = classNode.name?.text;
16349
+ const { classNode, sourceFile: parentSf, path: resolvedPath, factoryName } = ancestor;
16350
+ const className = classNode.name?.text ?? factoryName;
16269
16351
  const parentStaticMembers = classNode.members.filter(isStaticGetter);
16270
16352
  let resolvedClassNode = classNode;
16271
16353
  let resolvedStaticMembers = parentStaticMembers;
@@ -18121,13 +18203,17 @@ const getPackageJsonRecommendations = (config, compilerCtx) => {
18121
18203
  const loaderBundle = config.outputTargets.find(isOutputTargetLoaderBundle);
18122
18204
  const standalone = config.outputTargets.find(isOutputTargetStandalone);
18123
18205
  const types = config.outputTargets.find(isOutputTargetTypes);
18206
+ const srcIndexPath = join$2(config.srcDir, "index.ts");
18207
+ const hasSrcIndex = compilerCtx.fs.accessSync(srcIndexPath);
18124
18208
  const moduleOptions = [];
18125
- if (loaderBundle?.dir) moduleOptions.push(normalizePath(relative$1(config.rootDir, join$2(loaderBundle.dir, "index.js"))));
18209
+ if (loaderBundle?.dir) {
18210
+ const loaderEntry = hasSrcIndex ? join$2(loaderBundle.dir, "index.js") : join$2(loaderBundle.dir, "esm", "loader.js");
18211
+ moduleOptions.push(normalizePath(relative$1(config.rootDir, loaderEntry)));
18212
+ }
18126
18213
  if (standalone?.dir) moduleOptions.push(normalizePath(relative$1(config.rootDir, join$2(standalone.dir, "index.js"))));
18127
18214
  const typesOptions = [];
18128
18215
  if (types?.dir) {
18129
- const srcIndexPath = join$2(config.srcDir, "index.ts");
18130
- if (compilerCtx.fs.accessSync(srcIndexPath)) typesOptions.push(normalizePath(relative$1(config.rootDir, join$2(types.dir, "index.d.ts"))));
18216
+ if (hasSrcIndex) typesOptions.push(normalizePath(relative$1(config.rootDir, join$2(types.dir, "index.d.ts"))));
18131
18217
  else {
18132
18218
  if (loaderBundle) typesOptions.push(normalizePath(relative$1(config.rootDir, join$2(types.dir, "loader.d.ts"))));
18133
18219
  if (standalone) typesOptions.push(normalizePath(relative$1(config.rootDir, join$2(types.dir, "standalone.d.ts"))));
@@ -18145,6 +18231,31 @@ const getPackageJsonRecommendations = (config, compilerCtx) => {
18145
18231
  };
18146
18232
  };
18147
18233
  /**
18234
+ * Collect the rootDir-relative directories of every configured output target that must
18235
+ * ship in the published npm package for consumers (and their tooling) to be able to use
18236
+ * it - as opposed to dev-only artifacts like `www`.
18237
+ * @param config The validated Stencil configuration
18238
+ * @param buildCtx The build context (used to check whether there are actually component
18239
+ * assets to copy - the `assets` output target is otherwise a no-op and its directory is
18240
+ * never written)
18241
+ * @returns a de-duplicated list of rootDir-relative directory paths
18242
+ */
18243
+ const getDistributableOutputDirs = (config, buildCtx) => {
18244
+ const loaderBundle = config.outputTargets.find(isOutputTargetLoaderBundle);
18245
+ const standalone = config.outputTargets.find(isOutputTargetStandalone);
18246
+ const types = config.outputTargets.find(isOutputTargetTypes);
18247
+ const collections = config.outputTargets.filter(isOutputTargetCollection);
18248
+ const assets = buildCtx.components.some((cmp) => cmp.assetsDirs != null && cmp.assetsDirs.length > 0) ? config.outputTargets.find(isOutputTargetAssets) : void 0;
18249
+ const dirs = [
18250
+ loaderBundle?.dir,
18251
+ standalone?.dir,
18252
+ types?.dir,
18253
+ assets?.dir,
18254
+ ...collections.map((c) => c.dir)
18255
+ ].filter((dir) => !!dir).map((dir) => normalizePath(relative$1(config.rootDir, dir)));
18256
+ return Array.from(new Set(dirs));
18257
+ };
18258
+ /**
18148
18259
  * Validate the package.json file for a project, checking that various fields
18149
18260
  * are set correctly for the currently-configured output targets.
18150
18261
  *
@@ -18158,8 +18269,8 @@ const getPackageJsonRecommendations = (config, compilerCtx) => {
18158
18269
  const validateBuildPackageJson = async (config, compilerCtx, buildCtx) => {
18159
18270
  if (config.watch || config.devMode || buildCtx.packageJson == null) return;
18160
18271
  validatePackageJson(config, compilerCtx, buildCtx);
18161
- const stencilCollectionOutputTargets = config.outputTargets.filter(isOutputTargetCollection);
18162
- await Promise.all(stencilCollectionOutputTargets.map((stencilCollectionOT) => validateCollectionFields(config, compilerCtx, buildCtx, stencilCollectionOT)));
18272
+ await validateFilesField(config, compilerCtx, buildCtx);
18273
+ config.outputTargets.filter(isOutputTargetCollection).forEach((stencilCollectionOT) => validateCollectionField(config, compilerCtx, buildCtx, stencilCollectionOT));
18163
18274
  };
18164
18275
  /**
18165
18276
  * Validates a project's package.json fields against recommended values
@@ -18277,40 +18388,49 @@ const validateTypeField = (config, compilerCtx, buildCtx, recommendations) => {
18277
18388
  if (recommendations.moduleOptions.length > 0 && currentType !== "module") packageJsonWarn(config, compilerCtx, buildCtx, `package.json "type" property should be set to "module".`, "\"type\"");
18278
18389
  };
18279
18390
  /**
18280
- * Validate package.json contents specific to the `collection` output target,
18281
- * checking that the `files` array and `collection` field are set correctly.
18282
- * @param config the Stencil configuration associated with the project being compiled
18283
- * @param compilerCtx the current compiler context
18284
- * @param buildCtx the context associated with the current build
18285
- * @param outputTarget the collection output target to validate against
18391
+ * Check if a rootDir-relative directory is covered by a package.json `files` array,
18392
+ * either directly or via a parent directory entry (e.g. "dist/" covers "dist/types/").
18393
+ * @param files the package.json "files" array
18394
+ * @param dir the rootDir-relative directory to check for
18395
+ * @returns true if `dir` is covered by one of the `files` entries
18286
18396
  */
18287
- const validateCollectionFields = async (config, compilerCtx, buildCtx, outputTarget) => {
18288
- await Promise.all([validatePackageFiles(config, compilerCtx, buildCtx, outputTarget), validateCollectionField(config, compilerCtx, buildCtx, outputTarget)]);
18397
+ const isDirCoveredByFiles = (files, dir) => {
18398
+ const normalizedDir = dir.replace(/\/$/, "").replace(/^\.\//, "");
18399
+ return files.some((userPath) => {
18400
+ const normalizedUserPath = normalizePath(userPath).replace(/\/$/, "").replace(/^\.\//, "");
18401
+ if (normalizedUserPath === normalizedDir) return true;
18402
+ const userPathWithSlash = normalizedUserPath + "/";
18403
+ return normalizedDir.startsWith(userPathWithSlash);
18404
+ });
18289
18405
  };
18290
18406
  /**
18291
- * Validate that the `files` field in `package.json` contains directories and
18292
- * files that are necessary for the `collection` output target.
18407
+ * Validate that the `files` field in `package.json` covers every distributable output
18408
+ * directory (loader-bundle, standalone, types, collection) - not just one of them -
18409
+ * since `npm publish` needs all of them to produce a working package for consumers.
18293
18410
  * @param config the Stencil configuration associated with the project being compiled
18294
18411
  * @param compilerCtx the current compiler context
18295
18412
  * @param buildCtx the context associated with the current build
18296
- * @param outputTarget the collection output target to validate against
18297
18413
  */
18298
- const validatePackageFiles = async (config, compilerCtx, buildCtx, outputTarget) => {
18299
- if (!Array.isArray(buildCtx.packageJson.files)) return;
18300
- const actualDistDir = normalizePath(relative$1(config.rootDir, outputTarget.dir));
18301
- if (!buildCtx.packageJson.files.some((userPath) => {
18302
- const normalizedUserPath = normalizePath(userPath).replace(/\/$/, "").replace(/^\.\//, "");
18303
- const normalizedDistDir = actualDistDir.replace(/\/$/, "").replace(/^\.\//, "");
18304
- if (normalizedUserPath === normalizedDistDir) return true;
18305
- const userPathWithSlash = normalizedUserPath + "/";
18306
- if (normalizedDistDir.startsWith(userPathWithSlash)) return true;
18307
- return false;
18308
- })) {
18309
- const msg = `package.json "files" array must contain the distribution directory "${actualDistDir}/" when generating a distribution.`;
18414
+ const validateFilesField = async (config, compilerCtx, buildCtx) => {
18415
+ const distDirs = getDistributableOutputDirs(config, buildCtx);
18416
+ if (distDirs.length === 0) return;
18417
+ const formatDirs = (dirs) => dirs.map((dir) => `"${dir}/"`).join(", ");
18418
+ if (!Array.isArray(buildCtx.packageJson.files)) {
18419
+ const npmignorePath = join$2(dirname$1(config.packageJsonFilePath), ".npmignore");
18420
+ if (!await compilerCtx.fs.access(npmignorePath)) {
18421
+ const msg = `package.json is missing a "files" array. Without one, "npm publish" falls back to ".gitignore" to decide what to include, which will likely exclude ${distDirs.length > 1 ? "the distribution directories" : "the distribution directory"} ${formatDirs(distDirs)}. Add a "files" array covering ${distDirs.length > 1 ? "them" : "it"} to package.json (or add a ".npmignore").`;
18422
+ packageJsonWarn(config, compilerCtx, buildCtx, msg, `"files"`);
18423
+ }
18424
+ return;
18425
+ }
18426
+ const files = buildCtx.packageJson.files;
18427
+ const uncoveredDirs = distDirs.filter((dir) => !isDirCoveredByFiles(files, dir));
18428
+ if (uncoveredDirs.length > 0) {
18429
+ const msg = `package.json "files" array must contain the distribution ${uncoveredDirs.length > 1 ? "directories" : "directory"} ${formatDirs(uncoveredDirs)} when generating a distribution.`;
18310
18430
  packageJsonWarn(config, compilerCtx, buildCtx, msg, `"files"`);
18311
18431
  return;
18312
18432
  }
18313
- await Promise.all(buildCtx.packageJson.files.map(async (pkgFile) => {
18433
+ await Promise.all(files.map(async (pkgFile) => {
18314
18434
  if (!isGlob(pkgFile)) {
18315
18435
  const packageJsonDir = dirname$1(config.packageJsonFilePath);
18316
18436
  const absPath = join$2(packageJsonDir, pkgFile);
@@ -18425,6 +18545,24 @@ const validateBuildFiles = (config, compilerCtx, buildCtx) => {
18425
18545
  //#endregion
18426
18546
  //#region src/compiler/build/write-export-maps.ts
18427
18547
  /**
18548
+ * Create the shared {@link NpmPkgSet} used across a single `writeExportMaps` run.
18549
+ * @param buildCtx The build context to report a warning diagnostic on if `npm` can't be run
18550
+ * @returns A function that shells out to `npm pkg set`, swallowing failure after warning once
18551
+ */
18552
+ const createNpmPkgSet = (buildCtx) => {
18553
+ let npmAvailable = true;
18554
+ return (cmd) => {
18555
+ if (!npmAvailable) return;
18556
+ try {
18557
+ execSync(`npm pkg set ${cmd}`);
18558
+ } catch (e) {
18559
+ npmAvailable = false;
18560
+ const warn = buildWarn(buildCtx.diagnostics);
18561
+ warn.messageText = `Unable to generate "exports" map in package.json: the "npm" CLI could not be run (${e.message ?? e}). Set "generateExportMaps: false" in your Stencil config to silence this warning.`;
18562
+ }
18563
+ };
18564
+ };
18565
+ /**
18428
18566
  * Create export map entry point definitions for the `package.json` file using the npm CLI.
18429
18567
  *
18430
18568
  * In v5, this uses a "smart default" approach:
@@ -18435,15 +18573,17 @@ const validateBuildFiles = (config, compilerCtx, buildCtx) => {
18435
18573
  * - Generate per-component exports for standalone output
18436
18574
  *
18437
18575
  * @param config The validated Stencil config
18576
+ * @param compilerCtx The compiler context (used to detect a user-authored src/index.ts)
18438
18577
  * @param buildCtx The build context containing the components to generate export maps for
18439
18578
  */
18440
- const writeExportMaps = (config, buildCtx) => {
18579
+ const writeExportMaps = (config, compilerCtx, buildCtx) => {
18441
18580
  const loaderBundle = config.outputTargets.find(isOutputTargetLoaderBundle);
18442
18581
  const standalone = config.outputTargets.find(isOutputTargetStandalone);
18443
18582
  const types = config.outputTargets.find(isOutputTargetTypes);
18444
- generateRootExport(config, buildCtx, loaderBundle, standalone, types);
18445
- if (loaderBundle) generateLoaderExport(config, loaderBundle, types);
18446
- if (standalone) generateComponentExports(config, buildCtx, standalone);
18583
+ const npmPkgSet = createNpmPkgSet(buildCtx);
18584
+ generateRootExport(config, compilerCtx, buildCtx, loaderBundle, standalone, types, npmPkgSet);
18585
+ if (loaderBundle) generateLoaderExport(config, loaderBundle, types, npmPkgSet);
18586
+ if (standalone) generateComponentExports(config, buildCtx, standalone, npmPkgSet);
18447
18587
  };
18448
18588
  /**
18449
18589
  * Generate the root export `exports["."]`.
@@ -18453,28 +18593,32 @@ const writeExportMaps = (config, buildCtx) => {
18453
18593
  * - If valid, leave it alone
18454
18594
  * - If missing or invalid, set default (loader-bundle > standalone priority)
18455
18595
  * @param config The validated Stencil config
18596
+ * @param compilerCtx The compiler context (used to detect a user-authored src/index.ts)
18456
18597
  * @param buildCtx The build context containing the components to generate export maps for
18457
18598
  * @param loaderBundle The loader-bundle output target, if it exists
18458
18599
  * @param standalone The standalone output target, if it exists
18459
18600
  * @param types The types output target, if it exists
18601
+ * @param npmPkgSet Function used to run `npm pkg set`, tolerating an unavailable npm CLI
18460
18602
  */
18461
- const generateRootExport = (config, buildCtx, loaderBundle, standalone, types) => {
18603
+ const generateRootExport = (config, compilerCtx, buildCtx, loaderBundle, standalone, types, npmPkgSet) => {
18462
18604
  if (!loaderBundle && !standalone) return;
18605
+ const hasSrcIndex = compilerCtx.fs.accessSync(join$2(config.srcDir, "index.ts"));
18606
+ const rootUsesEmptyLoaderIndex = !!loaderBundle && !hasSrcIndex;
18463
18607
  const currentImport = ((buildCtx.packageJson?.exports)?.["."])?.import;
18464
18608
  if (!(currentImport && isValidRootExport(config, currentImport, loaderBundle, standalone))) {
18465
18609
  const primaryDir = loaderBundle?.dir ?? standalone?.dir;
18466
18610
  if (primaryDir) {
18467
- const importPath = normalizePath(relative$1(config.rootDir, join$2(primaryDir, "index.js")));
18468
- execSync(`npm pkg set "exports[.][import]"="${importPath}"`);
18611
+ const entryFile = rootUsesEmptyLoaderIndex ? join$2("esm", "loader.js") : "index.js";
18612
+ npmPkgSet(`"exports[.][import]"="${normalizePath(relative$1(config.rootDir, join$2(primaryDir, entryFile)))}"`);
18469
18613
  if (loaderBundle?.cjs) {
18470
- const requirePath = normalizePath(relative$1(config.rootDir, join$2(loaderBundle.dir, "index.cjs")));
18471
- execSync(`npm pkg set "exports[.][require]"="${requirePath}"`);
18614
+ const cjsEntryFile = rootUsesEmptyLoaderIndex ? join$2("cjs", "loader.cjs") : "index.cjs";
18615
+ npmPkgSet(`"exports[.][require]"="${normalizePath(relative$1(config.rootDir, join$2(loaderBundle.dir, cjsEntryFile)))}"`);
18472
18616
  }
18473
18617
  }
18474
18618
  }
18475
18619
  if (types?.dir) {
18476
- const typesPath = normalizePath(relative$1(config.rootDir, join$2(types.dir, "index.d.ts")));
18477
- execSync(`npm pkg set "exports[.][types]"="${typesPath}"`);
18620
+ const typesFile = rootUsesEmptyLoaderIndex ? "loader.d.ts" : "index.d.ts";
18621
+ npmPkgSet(`"exports[.][types]"="${normalizePath(relative$1(config.rootDir, join$2(types.dir, typesFile)))}"`);
18478
18622
  }
18479
18623
  };
18480
18624
  /**
@@ -18517,20 +18661,16 @@ const ensureRelativePrefix = (path) => {
18517
18661
  * @param config The validated Stencil config
18518
18662
  * @param loaderBundle The loader-bundle output target
18519
18663
  * @param types The types output target, if it exists
18664
+ * @param npmPkgSet Function used to run `npm pkg set`, tolerating an unavailable npm CLI
18520
18665
  */
18521
- const generateLoaderExport = (config, loaderBundle, types) => {
18666
+ const generateLoaderExport = (config, loaderBundle, types, npmPkgSet) => {
18522
18667
  const esmDir = join$2(loaderBundle.dir, "esm");
18523
- const esmLoaderPath = ensureRelativePrefix(normalizePath(relative$1(config.rootDir, join$2(esmDir, "loader.js"))));
18524
- execSync(`npm pkg set "exports[./loader][import]"="${esmLoaderPath}"`);
18668
+ npmPkgSet(`"exports[./loader][import]"="${ensureRelativePrefix(normalizePath(relative$1(config.rootDir, join$2(esmDir, "loader.js"))))}"`);
18525
18669
  if (loaderBundle.cjs) {
18526
18670
  const cjsDir = join$2(loaderBundle.dir, "cjs");
18527
- const cjsLoaderPath = ensureRelativePrefix(normalizePath(relative$1(config.rootDir, join$2(cjsDir, "loader.cjs"))));
18528
- execSync(`npm pkg set "exports[./loader][require]"="${cjsLoaderPath}"`);
18529
- }
18530
- if (types?.dir) {
18531
- const typesPath = ensureRelativePrefix(normalizePath(relative$1(config.rootDir, join$2(types.dir, "loader.d.ts"))));
18532
- execSync(`npm pkg set "exports[./loader][types]"="${typesPath}"`);
18671
+ npmPkgSet(`"exports[./loader][require]"="${ensureRelativePrefix(normalizePath(relative$1(config.rootDir, join$2(cjsDir, "loader.cjs"))))}"`);
18533
18672
  }
18673
+ if (types?.dir) npmPkgSet(`"exports[./loader][types]"="${ensureRelativePrefix(normalizePath(relative$1(config.rootDir, join$2(types.dir, "loader.d.ts"))))}"`);
18534
18674
  };
18535
18675
  /**
18536
18676
  * Generate per-component exports for standalone output.
@@ -18538,13 +18678,14 @@ const generateLoaderExport = (config, loaderBundle, types) => {
18538
18678
  * @param config The validated Stencil config
18539
18679
  * @param buildCtx The build context containing the components to generate export maps for
18540
18680
  * @param standalone The standalone output target
18681
+ * @param npmPkgSet Function used to run `npm pkg set`, tolerating an unavailable npm CLI
18541
18682
  */
18542
- const generateComponentExports = (config, buildCtx, standalone) => {
18683
+ const generateComponentExports = (config, buildCtx, standalone, npmPkgSet) => {
18543
18684
  let outDir = relative$1(config.rootDir, standalone.dir);
18544
18685
  if (!outDir.startsWith(".")) outDir = "./" + outDir;
18545
18686
  buildCtx.components.forEach((cmp) => {
18546
- execSync(`npm pkg set "exports[./${cmp.tagName}][import]"="${outDir}/${cmp.tagName}.js"`);
18547
- execSync(`npm pkg set "exports[./${cmp.tagName}][types]"="${outDir}/${cmp.tagName}.d.ts"`);
18687
+ npmPkgSet(`"exports[./${cmp.tagName}][import]"="${outDir}/${cmp.tagName}.js"`);
18688
+ npmPkgSet(`"exports[./${cmp.tagName}][types]"="${outDir}/${cmp.tagName}.d.ts"`);
18548
18689
  });
18549
18690
  };
18550
18691
  //#endregion
@@ -18568,7 +18709,7 @@ const writeBuild = async (config, compilerCtx, buildCtx) => {
18568
18709
  await compilerCtx.cache.commit();
18569
18710
  buildCtx.debug(`in-memory-fs: ${compilerCtx.fs.getMemoryStats()}`);
18570
18711
  buildCtx.debug(`cache: ${compilerCtx.cache.getMemoryStats()}`);
18571
- if (config.generateExportMaps) writeExportMaps(config, buildCtx);
18712
+ if (config.generateExportMaps && !config.devMode) writeExportMaps(config, compilerCtx, buildCtx);
18572
18713
  await outputServiceWorkers(config, buildCtx);
18573
18714
  await validateBuildFiles(config, compilerCtx, buildCtx);
18574
18715
  } catch (e) {
@@ -20603,10 +20744,11 @@ const validateConfig = (userConfig = {}, bootstrapConfig) => {
20603
20744
  const devMode = isBoolean$1(config.devMode) ? config.devMode : false;
20604
20745
  config._isTesting = !!(process.env.VITEST || process.env.PLAYWRIGHT_TEST || process.env.TEST_WORKER_INDEX || process.env.TEST_PARALLEL_INDEX || process.env.NODE_ENV === "test");
20605
20746
  const sys = config.sys ?? bootstrapConfig.sys ?? createNodeSys({ logger });
20747
+ const hasExplicitGlobalStyleWithInput = (config.outputTargets || []).some((o) => isOutputTargetGlobalStyle(o) && isString$1(o.input));
20606
20748
  if ((!isString$1(config.globalStyle) || !isString$1(config.globalScript)) && typeof sys.accessSync === "function") {
20607
20749
  const preRootDir = typeof config.rootDir !== "string" ? "/" : config.rootDir;
20608
20750
  const srcDir = typeof config.srcDir === "string" ? isAbsolute$1(config.srcDir) ? config.srcDir : join$1(preRootDir, config.srcDir) : join$1(preRootDir, "src");
20609
- if (!isString$1(config.globalStyle)) for (const ext of STYLE_EXT) {
20751
+ if (!isString$1(config.globalStyle) && !hasExplicitGlobalStyleWithInput) for (const ext of STYLE_EXT) {
20610
20752
  const candidate = join$1(srcDir, `global.${ext}`);
20611
20753
  if (sys.accessSync(candidate)) {
20612
20754
  config.globalStyle = candidate;
@@ -20626,7 +20768,7 @@ const validateConfig = (userConfig = {}, bootstrapConfig) => {
20626
20768
  ...config,
20627
20769
  devMode,
20628
20770
  compat: config.compat || {},
20629
- generateExportMaps: isBoolean$1(config.generateExportMaps) ? config.generateExportMaps : false,
20771
+ generateExportMaps: isBoolean$1(config.generateExportMaps) ? config.generateExportMaps : !isString$1(config.configPath),
20630
20772
  hydratedFlag: validateHydrated(config),
20631
20773
  logLevel,
20632
20774
  logger,