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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -17,6 +17,7 @@ import postcssSelectorParser from "postcss-selector-parser";
17
17
  import MagicString from "magic-string";
18
18
  import { rolldown } from "rolldown";
19
19
  import resolve$1 from "resolve";
20
+ import { minifySync } from "rolldown/utils";
20
21
  import { minify } from "terser";
21
22
  import { SelectorType, parse, stringify } from "css-what";
22
23
  import { execFile } from "node:child_process";
@@ -72,12 +73,13 @@ const restoreSafeSelector = (placeholders, content) => {
72
73
  const _polyfillHost = "-shadowcsshost";
73
74
  const _polyfillSlotted = "-shadowcssslotted";
74
75
  const _polyfillHostContext = "-shadowcsscontext";
76
+ const _parenSuffix = ")(?:\\(((?:\\([^)(]*\\)|[^)(]*)+?)\\))?([^,{]*)";
75
77
  let _cssColonHostRe;
76
78
  let _cssColonHostContextRe;
77
79
  let _cssColonSlottedRe;
78
- const getCssColonHostRe = () => _cssColonHostRe ??= /* @__PURE__ */ new RegExp("(-shadowcsshost)(?:\\(((?:\\([^)(]*\\)|[^)(]*)+?)\\))?([^,{]*)", "gim");
79
- const getCssColonHostContextRe = () => _cssColonHostContextRe ??= /* @__PURE__ */ new RegExp("(-shadowcsscontext)(?:\\(((?:\\([^)(]*\\)|[^)(]*)+?)\\))?([^,{]*)", "gim");
80
- const getCssColonSlottedRe = () => _cssColonSlottedRe ??= /* @__PURE__ */ new RegExp("(-shadowcssslotted)(?:\\(((?:\\([^)(]*\\)|[^)(]*)+?)\\))?([^,{]*)", "gim");
80
+ const getCssColonHostRe = () => _cssColonHostRe ??= new RegExp("(-shadowcsshost" + _parenSuffix, "gim");
81
+ const getCssColonHostContextRe = () => _cssColonHostContextRe ??= new RegExp("(-shadowcsscontext" + _parenSuffix, "gim");
82
+ const getCssColonSlottedRe = () => _cssColonSlottedRe ??= new RegExp("(-shadowcssslotted" + _parenSuffix, "gim");
81
83
  const _polyfillHostNoCombinator = "-shadowcsshost-no-combinator";
82
84
  const _polyfillHostNoCombinatorRe = /-shadowcsshost-no-combinator([^\s]*)/;
83
85
  const _shadowDOMSelectorsRe = [/::shadow/g, /::content/g];
@@ -460,9 +462,9 @@ const scopeCss = (cssText, scopeId, commentOriginalSelector) => {
460
462
  };
461
463
  //#endregion
462
464
  //#region src/version.ts
463
- const version = "5.0.0-beta.0";
464
- const buildId = "1787699045";
465
- const vermoji = "🐥";
465
+ const version = "5.0.0-beta.1";
466
+ const buildId = "1788442714";
467
+ const vermoji = "🚓";
466
468
  /**
467
469
  * Get the installed version of a tool/dependency.
468
470
  * Handles packages with exports maps that point to subdirectories.
@@ -1020,6 +1022,7 @@ const REGISTER_HOST = "__stencil_registerHost";
1020
1022
  const H = "__stencil_h";
1021
1023
  const TRANSFORM_TAG = "__stencil_transformTag";
1022
1024
  const GET_REGISTRY = "__stencil_getRegistry";
1025
+ const INJECT_SIDE_EFFECT_STYLE = "__stencil_injectSideEffectStyle";
1023
1026
  const RUNTIME_APIS = {
1024
1027
  createEvent: `createEvent as ${CREATE_EVENT}`,
1025
1028
  defineCustomElement: `defineCustomElement as ${DEFINE_CUSTOM_ELEMENT}`,
@@ -2421,7 +2424,7 @@ function addTagTransformToCssString(cssCode, tagNames) {
2421
2424
  rule.selectors = rule.selectors.map((sel) => {
2422
2425
  const parsedSelector = postcssSelectorParser().astSync(sel);
2423
2426
  parsedSelector.walkTags((tag) => {
2424
- if (tagNames.includes(tag.value)) tag.value = "${__stencil_transformTag(\"" + tag.value + "\")}";
2427
+ if (tagNames.includes(tag.value)) tag.value = "${" + TRANSFORM_TAG + "(\"" + tag.value + "\")}";
2425
2428
  });
2426
2429
  return parsedSelector.toString();
2427
2430
  });
@@ -3354,9 +3357,9 @@ const runPluginTransformsEsmImports = async (config, compilerCtx, buildCtx, code
3354
3357
  return transformResults;
3355
3358
  };
3356
3359
  //#endregion
3357
- //#region src/compiler/style/optimize-css.ts
3360
+ //#region src/compiler/style/optimize-style-css.ts
3358
3361
  const getCssToolVersions = () => `autoprefixer@${getToolVersion("autoprefixer")}_postcss@${getToolVersion("postcss")}`;
3359
- const optimizeCss$1 = async (config, compilerCtx, diagnostics, styleText, filePath) => {
3362
+ const optimizeStyleCss = async (config, compilerCtx, diagnostics, styleText, filePath) => {
3360
3363
  if (typeof styleText !== "string" || !styleText.length) return styleText;
3361
3364
  if ((config.autoprefixCss === false || config.autoprefixCss === null) && !config.minifyCss) return styleText;
3362
3365
  if (typeof filePath === "string") filePath = normalizePath(filePath);
@@ -3441,7 +3444,7 @@ const collectAndBuildComponentGlobalStyles = async (config, compilerCtx, buildCt
3441
3444
  if (!result) continue;
3442
3445
  const cssCode = typeof result === "string" ? result : result.code;
3443
3446
  if (!cssCode) continue;
3444
- const optimized = await optimizeCss$1(config, compilerCtx, buildCtx.diagnostics, cssCode, path);
3447
+ const optimized = await optimizeStyleCss(config, compilerCtx, buildCtx.diagnostics, cssCode, path);
3445
3448
  compilerCtx.globalStyleCache.set(path, optimized);
3446
3449
  parts.push(optimized);
3447
3450
  }
@@ -3523,7 +3526,7 @@ const buildGlobalStyleFromInput = async (config, compilerCtx, buildCtx, inputPat
3523
3526
  }
3524
3527
  if (hasStencilGlobalsImport(cssCode)) cssCode = await resolveStencilGlobalsImport(cssCode, config, compilerCtx, buildCtx, normalizedPath);
3525
3528
  if (hasStencilHydrateImport(cssCode)) cssCode = resolveStencilHydrateImport(cssCode, config, buildCtx);
3526
- const optimizedCss = await optimizeCss$1(config, compilerCtx, buildCtx.diagnostics, cssCode, normalizedPath);
3529
+ const optimizedCss = await optimizeStyleCss(config, compilerCtx, buildCtx.diagnostics, cssCode, normalizedPath);
3527
3530
  compilerCtx.globalStyleCache.set(normalizedPath, optimizedCss);
3528
3531
  if (Array.isArray(dependencies)) {
3529
3532
  const cssModuleImports = compilerCtx.cssModuleImports.get(normalizedPath) || [];
@@ -3795,9 +3798,22 @@ const appendGlobalStyles = async (config, compilerCtx, buildCtx, s, platform) =>
3795
3798
  * @param s a `MagicString` to append the generated constant onto
3796
3799
  */
3797
3800
  const appendBuildConditionals = (config, buildConditionals, s) => {
3798
- const buildData = Object.keys(buildConditionals).sort().map((key) => key + ": " + JSON.stringify(buildConditionals[key])).join(", ");
3801
+ const buildData = getSortedBuildConditionalEntries(buildConditionals).map(([key, value]) => key + ": " + JSON.stringify(value)).join(", ");
3799
3802
  s.append(`export const BUILD = /* ${config.fsNamespace} */ { ${buildData} };\n`);
3800
3803
  };
3804
+ /**
3805
+ * Builds a `<flag> -> literal-value-as-source-text` map for every build conditional, for use by
3806
+ * {@link buildConditionalsPlugin} to replace `BUILD.<flag>` reads with their literal values.
3807
+ *
3808
+ * @param buildConditionals the build conditionals for this build
3809
+ * @returns a flag name > literal source text map
3810
+ */
3811
+ const getBuildConditionalsLiterals = (buildConditionals) => {
3812
+ const literals = /* @__PURE__ */ new Map();
3813
+ for (const [key, value] of getSortedBuildConditionalEntries(buildConditionals)) literals.set(key, JSON.stringify(value));
3814
+ return literals;
3815
+ };
3816
+ const getSortedBuildConditionalEntries = (buildConditionals) => Object.keys(buildConditionals).sort().map((key) => [key, buildConditionals[key]]);
3801
3817
  const appendEnv = (config, s) => {
3802
3818
  s.append(`export const Env = /* ${config.fsNamespace} */ ${JSON.stringify(config.env)};\n`);
3803
3819
  };
@@ -3912,7 +3928,6 @@ const coreResolvePlugin = (config, compilerCtx, platform, externalRuntime, lazyL
3912
3928
  const hydratedFlag = config.hydratedFlag;
3913
3929
  const hydratedFlagHead = hydratedFlag ? getHydratedFlagHead(hydratedFlag) : null;
3914
3930
  const hydratedReplacements = hydratedFlag && hydratedFlagHead !== "{visibility:hidden}.hydrated{visibility:inherit}" ? buildHydratedReplacements(hydratedFlag, hydratedFlagHead) : null;
3915
- const escapeRegex = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3916
3931
  return {
3917
3932
  name: "coreResolvePlugin",
3918
3933
  resolveId: {
@@ -3952,7 +3967,7 @@ const coreResolvePlugin = (config, compilerCtx, platform, externalRuntime, lazyL
3952
3967
  }
3953
3968
  },
3954
3969
  load: {
3955
- filter: { id: new RegExp(`^(${escapeRegex(internalClient)}|${escapeRegex(internalSsr)})(\\?.*)?$`) },
3970
+ filter: { id: getStencilInternalModuleFilter(internalClient, internalSsr) },
3956
3971
  async handler(filePath) {
3957
3972
  if (filePath && !filePath.startsWith("\0")) {
3958
3973
  filePath = normalizeFsPath(filePath);
@@ -3984,6 +3999,19 @@ export const Build = {
3984
3999
  }
3985
4000
  };
3986
4001
  };
4002
+ /**
4003
+ * Builds a filter regex matching only Stencil's own resolved internal runtime module(s),
4004
+ * tolerant of query-string suffixes (e.g. `?app-data=conditional` for lazy builds). Shared by
4005
+ * any plugin that needs to scope its work to Stencil's own runtime code, rather than the rest
4006
+ * of a downstream app's bundle.
4007
+ *
4008
+ * @param moduleIds absolute paths to Stencil's resolved internal runtime module(s)
4009
+ * @returns a regex suitable for a rolldown hook's `id` filter
4010
+ */
4011
+ const getStencilInternalModuleFilter = (...moduleIds) => {
4012
+ const escapeRegex = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
4013
+ return new RegExp(`^(${moduleIds.map(escapeRegex).join("|")})(\\?.*)?$`);
4014
+ };
3987
4015
  const getStencilInternalModule = (config, compilerExe, internalModule) => {
3988
4016
  if (isRemoteUrl(compilerExe)) return normalizePath(config.sys.getLocalModulePath({
3989
4017
  rootDir: config.rootDir,
@@ -4016,6 +4044,53 @@ const buildHydratedReplacements = (hydratedFlag, hydratedFlagHead) => {
4016
4044
  return replacements;
4017
4045
  };
4018
4046
  //#endregion
4047
+ //#region src/compiler/bundle/build-conditionals-plugin.ts
4048
+ const BUILD_FLAG_RE = /\bBUILD(?:\$\d+)?\.([A-Za-z_$][\w$]*)\b/g;
4049
+ /**
4050
+ * Scoped by `id` to Stencil's own resolved internal runtime module(s) only
4051
+ * and only when the `jsMinifier` = `oxc`.
4052
+ *
4053
+ * Replaces `BUILD.<flag>` reads with their literal values, so a minifier's basic literal-boolean
4054
+ * folding (e.g. `false && x` -> nothing) can eliminate the resulting dead branches.
4055
+ * Oxc cannot dead-code-eliminate using `BUILD.<flag>` reads directly.
4056
+ *
4057
+ * @param config the Stencil configuration for the project
4058
+ * @param buildConditionals the build conditionals for this build
4059
+ * @returns a rolldown plugin, or null if this build doesn't need it
4060
+ */
4061
+ const buildConditionalsPlugin = (config, buildConditionals) => {
4062
+ if (config.jsMinifier !== "oxc" || !buildConditionals) return null;
4063
+ const literals = getBuildConditionalsLiterals(buildConditionals);
4064
+ if (literals.size === 0) return null;
4065
+ const compilerExe = config.sys.getCompilerExecutingPath();
4066
+ const internalClient = getStencilInternalModule(config, compilerExe, "client/runtime.js");
4067
+ const internalSsr = getStencilInternalModule(config, compilerExe, "server/index.mjs");
4068
+ return {
4069
+ name: "stencil-build-conditionals",
4070
+ transform: {
4071
+ filter: {
4072
+ id: getStencilInternalModuleFilter(internalClient, internalSsr),
4073
+ code: /\bBUILD(?:\$\d+)?\.\w/
4074
+ },
4075
+ handler(code) {
4076
+ const s = new MagicString(code);
4077
+ let didReplace = false;
4078
+ for (const match of code.matchAll(BUILD_FLAG_RE)) {
4079
+ const literal = literals.get(match[1]);
4080
+ if (literal === void 0) continue;
4081
+ s.overwrite(match.index, match.index + match[0].length, literal);
4082
+ didReplace = true;
4083
+ }
4084
+ if (!didReplace) return null;
4085
+ return {
4086
+ code: s.toString(),
4087
+ map: s.generateMap({ hires: true })
4088
+ };
4089
+ }
4090
+ }
4091
+ };
4092
+ };
4093
+ //#endregion
4019
4094
  //#region src/compiler/bundle/constants.ts
4020
4095
  const DEV_MODULE_DIR = `~dev-module`;
4021
4096
  //#endregion
@@ -4250,9 +4325,10 @@ const extTransformsPlugin = (config, compilerCtx, buildCtx) => {
4250
4325
  if (typeof code !== "string") compilerCtx.cssTransformCache.set(id, null);
4251
4326
  else {
4252
4327
  const pluginTransforms = await runPluginTransformsEsmImports(config, compilerCtx, buildCtx, code, filePath);
4328
+ const cssInput = data.tag ? pluginTransforms.code : inlineRelativeCssUrlAssets(pluginTransforms.code, filePath);
4253
4329
  const cssTransformResults = await compilerCtx.worker.transformCssToEsm({
4254
4330
  file: pluginTransforms.id,
4255
- input: pluginTransforms.code,
4331
+ input: cssInput,
4256
4332
  tag: data.tag,
4257
4333
  tags: buildCtx.components.map((c) => c.tagName),
4258
4334
  addTagTransformers: !!buildCtx.config.compat.additionalTagTransformers,
@@ -4340,13 +4416,46 @@ const extTransformsPlugin = (config, compilerCtx, buildCtx) => {
4340
4416
  return {
4341
4417
  code: cacheEntry.cssTransformOutput.output,
4342
4418
  map: cacheEntry.cssTransformOutput.map,
4343
- moduleSideEffects: false
4419
+ moduleSideEffects: cacheEntry.cssTransformOutput.moduleSideEffects ?? false
4344
4420
  };
4345
4421
  }
4346
4422
  return null;
4347
4423
  }
4348
4424
  };
4349
4425
  };
4426
+ /**
4427
+ * 3rd-party stylesheets with relative `url(./font.ttf)` (font-face `src`, a background-image, ...)
4428
+ * have nowhere to resolve that path once this CSS is self-injected as a CSSStyleSheet:
4429
+ * there's no bundler output location for it to be relative *to*.
4430
+ * Inlining the referenced file as a `data:` URI sidesteps needing one - no separate asset
4431
+ * to serve, works the same regardless of how/where the final bundle is deployed.
4432
+ */
4433
+ const CSS_URL_RE = /url\(\s*(['"]?)([^'"()]+)\1\s*\)/g;
4434
+ const CSS_URL_ASSET_MIME_TYPES = {
4435
+ ttf: "font/ttf",
4436
+ otf: "font/otf",
4437
+ woff: "font/woff",
4438
+ woff2: "font/woff2",
4439
+ eot: "application/vnd.ms-fontobject",
4440
+ png: "image/png",
4441
+ jpg: "image/jpeg",
4442
+ jpeg: "image/jpeg",
4443
+ gif: "image/gif",
4444
+ svg: "image/svg+xml"
4445
+ };
4446
+ const inlineRelativeCssUrlAssets = (cssText, cssFilePath) => cssText.replace(CSS_URL_RE, (original, _quote, url) => {
4447
+ if (/^(data:|[a-z][a-z0-9+.-]*:|\/\/|\/)/i.test(url)) return original;
4448
+ const cleanUrl = url.split(/[?#]/)[0];
4449
+ const ext = cleanUrl.split(".").pop()?.toLowerCase();
4450
+ const mimeType = ext && CSS_URL_ASSET_MIME_TYPES[ext];
4451
+ if (!mimeType) return original;
4452
+ try {
4453
+ const assetPath = resolve(dirname(cssFilePath), cleanUrl);
4454
+ return `url("data:${mimeType};base64,${readFileSync(assetPath).toString("base64")}")`;
4455
+ } catch {
4456
+ return original;
4457
+ }
4458
+ });
4350
4459
  //#endregion
4351
4460
  //#region src/compiler/bundle/file-load-plugin.ts
4352
4461
  const RESOLVE_EXTENSIONS = [
@@ -4482,11 +4591,13 @@ const pluginHelper = (config, builtCtx, platform) => {
4482
4591
  handler(importee, importer) {
4483
4592
  if (importee.endsWith("/")) importee = importee.slice(0, -1);
4484
4593
  if (builtIns.has(importee)) {
4594
+ const alias = config.nodeResolve?.alias;
4595
+ if (alias?.[importee] ?? alias?.[`${importee}$`]) return null;
4485
4596
  let fromMsg = "";
4486
4597
  if (importer) fromMsg = ` from ${relative$1(config.rootDir, importer)}`;
4487
4598
  const diagnostic = buildError(builtCtx.diagnostics);
4488
4599
  diagnostic.header = `Node Polyfills Required`;
4489
- diagnostic.messageText = `For the import "${importee}" to be bundled${fromMsg}, ensure the "rolldown-plugin-node-polyfills" plugin is installed and added to the stencil config plugins (${platform}). Please see the bundling docs for more information.
4600
+ diagnostic.messageText = `For the import "${importee}" to be bundled${fromMsg}, ensure the "@rolldown/plugin-node-polyfills" plugin is installed and added to the stencil config plugins (${platform}). Please see the bundling docs for more information.
4490
4601
  Further information: https://stenciljs.com/docs/module-bundling`;
4491
4602
  }
4492
4603
  return null;
@@ -4713,7 +4824,59 @@ const userIndexPlugin = (config, compilerCtx) => {
4713
4824
  };
4714
4825
  };
4715
4826
  //#endregion
4716
- //#region src/compiler/optimize/minify-js.ts
4827
+ //#region src/compiler/optimize/minify-js-oxc.ts
4828
+ /**
4829
+ * Performs the minification of JavaScript source using oxc
4830
+ * @param input the JavaScript source to minify
4831
+ * @param opts the options used by the minifier
4832
+ * @returns the resulting minified JavaScript
4833
+ */
4834
+ const minifyJsOxc = async (input, opts) => {
4835
+ const results = {
4836
+ output: input,
4837
+ sourceMap: null,
4838
+ diagnostics: []
4839
+ };
4840
+ try {
4841
+ const minifyResult = minifySync("module.js", input, opts);
4842
+ if (minifyResult.errors.length > 0) {
4843
+ loadMinifyJsOxcDiagnostics(results.diagnostics, minifyResult.errors);
4844
+ return results;
4845
+ }
4846
+ results.output = minifyResult.code;
4847
+ results.sourceMap = minifyResult.map ? {
4848
+ file: minifyResult.map.file ?? "module.js",
4849
+ mappings: minifyResult.map.mappings,
4850
+ names: minifyResult.map.names,
4851
+ sourceRoot: minifyResult.map.sourceRoot,
4852
+ sources: minifyResult.map.sources,
4853
+ sourcesContent: minifyResult.map.sourcesContent,
4854
+ version: minifyResult.map.version
4855
+ } : null;
4856
+ } catch (e) {
4857
+ if (e instanceof Error) console.log(e.stack);
4858
+ loadMinifyJsOxcDiagnostics(results.diagnostics, [{
4859
+ message: String(e),
4860
+ codeframe: null
4861
+ }]);
4862
+ }
4863
+ return results;
4864
+ };
4865
+ const loadMinifyJsOxcDiagnostics = (diagnostics, errors) => {
4866
+ for (const error of errors) diagnostics.push({
4867
+ level: "error",
4868
+ type: "build",
4869
+ language: "javascript",
4870
+ header: "Minify JS (oxc)",
4871
+ code: "",
4872
+ messageText: error.codeframe ?? error.message,
4873
+ absFilePath: void 0,
4874
+ relFilePath: void 0,
4875
+ lines: []
4876
+ });
4877
+ };
4878
+ //#endregion
4879
+ //#region src/compiler/optimize/minify-js-terser.ts
4717
4880
  /**
4718
4881
  * Performs the minification of JavaScript source
4719
4882
  * @param input the JavaScript source to minify
@@ -4732,14 +4895,6 @@ const minifyJs = async (input, opts) => {
4732
4895
  const mangleProperties = mangle.properties;
4733
4896
  if (mangleProperties && mangleProperties.regex) mangleProperties.regex = new RegExp(mangleProperties.regex);
4734
4897
  }
4735
- if (opts.sourceMap)
4736
- /**
4737
- * sourceMap, when used in conjunction with compress, can lead to sourcemaps that don't in every browser. despite
4738
- * there being a sourcemap spec, each browser has it's own tricks for trying to get sourcemaps to properly map
4739
- * minified JS back to its original form. for the most consistent results across all browsers, explicitly disable
4740
- * compress.
4741
- */
4742
- opts.compress = void 0;
4743
4898
  }
4744
4899
  try {
4745
4900
  const minifyResults = await minify(input, opts);
@@ -4845,7 +5000,8 @@ const optimizeModule = async (config, compilerCtx, opts) => {
4845
5000
  sourceMap: opts.sourceMap
4846
5001
  };
4847
5002
  const isDebug = config.logLevel === "debug";
4848
- const cacheKey = await compilerCtx.cache.createKey("optimizeModule", getToolVersion("terser"), opts, isDebug);
5003
+ const jsMinifier = config.jsMinifier;
5004
+ const cacheKey = await compilerCtx.cache.createKey("optimizeModule", jsMinifier, getToolVersion(jsMinifier === "oxc" ? "rolldown" : "terser"), opts, isDebug);
4849
5005
  const cachedContent = await compilerCtx.cache.get(cacheKey);
4850
5006
  if (cachedContent != null) {
4851
5007
  const cachedMap = await compilerCtx.cache.get(cacheKey + "Map");
@@ -4855,8 +5011,26 @@ const optimizeModule = async (config, compilerCtx, opts) => {
4855
5011
  sourceMap: cachedMap ? JSON.parse(cachedMap) : null
4856
5012
  };
4857
5013
  }
4858
- const minifyOpts = getTerserOptions(config, opts.sourceTarget, isDebug);
4859
5014
  const code = opts.input;
5015
+ const results = jsMinifier === "oxc" ? await minifyJsOxc(code, getOxcMinifyOptions(config, opts, isDebug)) : await compilerCtx.worker.prepareModule(code, getTerserMinifyOptions(config, opts, isDebug));
5016
+ if (results != null && typeof results.output === "string" && results.diagnostics.length === 0 && compilerCtx != null) {
5017
+ if (opts.isCore) results.output = results.output.replace(/disconnectedCallback\(\)\{\},/g, "");
5018
+ await compilerCtx.cache.put(cacheKey, results.output);
5019
+ if (results.sourceMap) await compilerCtx.cache.put(cacheKey + "Map", JSON.stringify(results.sourceMap));
5020
+ }
5021
+ return results;
5022
+ };
5023
+ /**
5024
+ * Builds the terser options for a specific module being optimized, layering the module's
5025
+ * `isCore`/source map needs on top of the baseline options from {@link getTerserOptions}.
5026
+ *
5027
+ * @param config the Stencil configuration file that was provided as a part of the build step
5028
+ * @param opts the options for the module being optimized
5029
+ * @param isDebug if true, set the necessary flags to produce readable, debuggable output
5030
+ * @returns the minification options to hand to terser
5031
+ */
5032
+ const getTerserMinifyOptions = (config, opts, isDebug) => {
5033
+ const minifyOpts = getTerserOptions(config, opts.sourceTarget, isDebug);
4860
5034
  if (config.sourceMap) minifyOpts.sourceMap = { content: opts.sourceMap != null ? {
4861
5035
  ...opts.sourceMap,
4862
5036
  version: 3
@@ -4878,13 +5052,7 @@ const optimizeModule = async (config, compilerCtx, opts) => {
4878
5052
  compressOpts.unsafe = true;
4879
5053
  compressOpts.unsafe_undefined = true;
4880
5054
  }
4881
- const results = await compilerCtx.worker.prepareModule(code, minifyOpts);
4882
- if (results != null && typeof results.output === "string" && results.diagnostics.length === 0 && compilerCtx != null) {
4883
- if (opts.isCore) results.output = results.output.replace(/disconnectedCallback\(\)\{\},/g, "");
4884
- await compilerCtx.cache.put(cacheKey, results.output);
4885
- if (results.sourceMap) await compilerCtx.cache.put(cacheKey + "Map", JSON.stringify(results.sourceMap));
4886
- }
4887
- return results;
5055
+ return minifyOpts;
4888
5056
  };
4889
5057
  /**
4890
5058
  * Builds a configuration object to be used by Terser for the purposes of minifying a user's JavaScript
@@ -4941,6 +5109,81 @@ function getTerserManglePropertiesConfig() {
4941
5109
  };
4942
5110
  }
4943
5111
  /**
5112
+ * Builds the oxc (rolldown minifier) options for a specific module being optimized.
5113
+ *
5114
+ * A few terser knobs have no oxc equivalent and so are dropped:
5115
+ * - `global_defs` (used to fold `supportsListenerOptions` to `true` in the core runtime bundle)
5116
+ * - `unsafe`/`unsafe_undefined`/`inline` compress passes
5117
+ * - `passes` (oxc's compressor iterates to a fix point automatically)
5118
+ *
5119
+ * @param config the Stencil configuration file that was provided as a part of the build step
5120
+ * @param opts the options for the module being optimized
5121
+ * @param isDebug if true, set the necessary flags to produce readable, debuggable output
5122
+ * @returns the minification options to hand to oxc
5123
+ */
5124
+ const getOxcMinifyOptions = (config, opts, isDebug) => {
5125
+ const minifyOpts = {
5126
+ module: true,
5127
+ sourcemap: !!config.sourceMap,
5128
+ inputMap: config.sourceMap ? opts.sourceMap ?? void 0 : void 0,
5129
+ mangleProps: getOxcManglePropertiesConfig(isDebug)
5130
+ };
5131
+ if (isDebug) {
5132
+ minifyOpts.mangle = false;
5133
+ minifyOpts.compress = {
5134
+ dropConsole: false,
5135
+ dropDebugger: false
5136
+ };
5137
+ minifyOpts.codegen = {
5138
+ removeWhitespace: false,
5139
+ legalComments: "inline"
5140
+ };
5141
+ } else {
5142
+ minifyOpts.mangle = { toplevel: true };
5143
+ minifyOpts.compress = { treeshake: {
5144
+ propertyReadSideEffects: false,
5145
+ manualPureFunctions: opts.isCore ? ["getHostRef"] : []
5146
+ } };
5147
+ }
5148
+ return minifyOpts;
5149
+ };
5150
+ /**
5151
+ * `ComponentRuntimeMeta` (the shape of a component class's static `cmpMeta` getter) is
5152
+ * produced by one compiled unit and read by another (e.g. a component's own chunk vs the
5153
+ * SSR/hydrate runtime, or a lazy component's entry chunk vs the core runtime chunk).
5154
+ *
5155
+ * Oxc's mangleProps` has been observed to mangle a property at its read site but leave the same
5156
+ * property's key unmangled silently breaking that read (e.g. `cmpMeta.$tagName$` reads as `undefined`).
5157
+ * Reserving these names avoids the mismatch entirely.
5158
+ *
5159
+ * Assess in time with rolldown / oxc updates via `cd test/build/output && pnpm test`
5160
+ */
5161
+ const RESERVED_CMP_META_PROPS = [
5162
+ "$flags$",
5163
+ "$tagName$",
5164
+ "$members$",
5165
+ "$listeners$",
5166
+ "$attrsToReflect$",
5167
+ "$watchers$",
5168
+ "$lazyBundleId$",
5169
+ "$serializers$",
5170
+ "$deserializers$"
5171
+ ];
5172
+ /**
5173
+ * Get baseline configuration for oxc's `mangleProps` option, mirroring
5174
+ * {@link getTerserManglePropertiesConfig}.
5175
+ *
5176
+ * @param isDebug if true, produce readable `_$name$_`-style output names instead of minified ones
5177
+ * @returns an object with our baseline property mangling configuration
5178
+ */
5179
+ function getOxcManglePropertiesConfig(isDebug) {
5180
+ return {
5181
+ include: /^\$.+\$$/,
5182
+ reserved: ["$hostElement$", ...RESERVED_CMP_META_PROPS],
5183
+ debug: isDebug
5184
+ };
5185
+ }
5186
+ /**
4944
5187
  * This method is likely to be called by a worker on the compiler context, rather than directly.
4945
5188
  * @param input the source code to minify
4946
5189
  * @param minifyOpts options to be used by the minifier
@@ -5089,7 +5332,7 @@ const buildWorker = async (config, compilerCtx, buildCtx, ctx, workerEntryPath)
5089
5332
  let code = entryPoint.code;
5090
5333
  const results = await optimizeModule(config, compilerCtx, {
5091
5334
  input: code,
5092
- sourceTarget: "es2017",
5335
+ sourceTarget: "es2022",
5093
5336
  isCore: false,
5094
5337
  minify: config.minifyJs,
5095
5338
  inlineHelpers: true
@@ -5393,6 +5636,7 @@ const getRolldownOptions = (config, compilerCtx, buildCtx, bundleOpts) => {
5393
5636
  },
5394
5637
  coreResolvePlugin(config, compilerCtx, bundleOpts.platform, !!bundleOpts.externalRuntime, bundleOpts.conditionals?.lazyLoad ?? false),
5395
5638
  appDataPlugin(config, compilerCtx, buildCtx, bundleOpts.conditionals, bundleOpts.platform),
5639
+ buildConditionalsPlugin(config, bundleOpts.conditionals),
5396
5640
  lazyComponentPlugin(buildCtx),
5397
5641
  loaderPlugin(bundleOpts.loader),
5398
5642
  userIndexPlugin(config, compilerCtx),
@@ -6569,10 +6813,6 @@ const addStaticImports = (rolldownChunkResults, bundleModules) => {
6569
6813
  const generateCjs = isCjsFormat(index) ? generateCaseClauseCjs : generateCaseClause;
6570
6814
  index.code = index.code.replace("/*!__STENCIL_STATIC_IMPORT_SWITCH__*/", `
6571
6815
  if (!hmrVersionId || !BUILD.hotModuleReplacement) {
6572
- const processMod = importedModule => {
6573
- cmpModules.set(bundleId, importedModule);
6574
- return importedModule[exportName];
6575
- }
6576
6816
  switch(bundleId) {
6577
6817
  ${bundleModules.map((mod) => generateCjs(mod.output.bundleId)).join("")}
6578
6818
  }
@@ -6606,7 +6846,7 @@ const generateCaseClause = (bundleId) => {
6606
6846
  case '${bundleId}':
6607
6847
  return import(
6608
6848
  /* webpackMode: "lazy" */
6609
- './${bundleId}.entry.js').then(processMod, consoleError);`;
6849
+ './${bundleId}.entry.js').then(onLoad, onError);`;
6610
6850
  };
6611
6851
  /**
6612
6852
  * Generate a 'case' clause to be used within a `switch` statement. The case clause generated will key-off the provided
@@ -6619,7 +6859,7 @@ const generateCaseClauseCjs = (bundleId) => {
6619
6859
  case '${bundleId}':
6620
6860
  return Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespace(require(
6621
6861
  /* webpackMode: "lazy" */
6622
- './${bundleId}.entry.js')); }).then(processMod, consoleError);`;
6862
+ './${bundleId}.entry.js')); }).then(onLoad, onError);`;
6623
6863
  };
6624
6864
  const generateLazyEntryModule = async (config, compilerCtx, buildCtx, rolldownResult, outputTargetType, destinations, sourceTarget, shouldMinify, isBrowserBuild) => {
6625
6865
  const entryModule = buildCtx.entryModules.find((em) => em.entryKey === rolldownResult.entryKey);
@@ -6847,7 +7087,7 @@ const generateCjs = async (config, compilerCtx, buildCtx, rolldownBuild, outputT
6847
7087
  const results = await generateRolldownOutput(rolldownBuild, esmOpts, config, buildCtx.entryModules);
6848
7088
  if (results != null) {
6849
7089
  const destinations = cjsOutputs.map((o) => o.cjsDir).filter((cjsDir) => typeof cjsDir === "string");
6850
- buildCtx.commonJsComponentBundle = await generateLazyModules(config, compilerCtx, buildCtx, outputTargetType, destinations, results, "es2017", false);
7090
+ buildCtx.commonJsComponentBundle = await generateLazyModules(config, compilerCtx, buildCtx, outputTargetType, destinations, results, "es2022", false);
6851
7091
  await generateShortcuts$1(compilerCtx, results, cjsOutputs);
6852
7092
  }
6853
7093
  }
@@ -12688,6 +12928,7 @@ const IS_CASE_SENSITIVE_FILE_NAMES = !(process.platform === "win32");
12688
12928
  //#endregion
12689
12929
  //#region src/compiler/sys/typescript/typescript-sys.ts
12690
12930
  const patchTsSystemFileSystem = (config, compilerSys, inMemoryFs, tsSys) => {
12931
+ if (!tsSys) return;
12691
12932
  const realpath = (path) => {
12692
12933
  const rp = compilerSys.realpathSync(path);
12693
12934
  if (isString$1(rp)) return rp;
@@ -12765,6 +13006,7 @@ const patchTsSystemFileSystem = (config, compilerSys, inMemoryFs, tsSys) => {
12765
13006
  return tsSys;
12766
13007
  };
12767
13008
  const patchTsSystemWatch = (compilerSystem, tsSys) => {
13009
+ if (!tsSys) return;
12768
13010
  tsSys.watchDirectory = (p, cb, recursive) => {
12769
13011
  const watcher = compilerSystem.watchDirectory(p, (filePath) => {
12770
13012
  cb(filePath);
@@ -12788,26 +13030,6 @@ const patchTypescript = (config, inMemoryFs) => {
12788
13030
  patchTsSystemFileSystem(config, config.sys, inMemoryFs, ts.sys);
12789
13031
  patchTsSystemWatch(config.sys, ts.sys);
12790
13032
  };
12791
- const patchTypeScriptSysMinimum = () => {
12792
- if (!ts.sys) ts.sys = {
12793
- args: [],
12794
- createDirectory: noop,
12795
- directoryExists: () => false,
12796
- exit: noop,
12797
- fileExists: () => false,
12798
- getCurrentDirectory: process.cwd,
12799
- getDirectories: () => [],
12800
- getExecutingFilePath: () => "./",
12801
- readDirectory: () => [],
12802
- readFile: noop,
12803
- newLine: "\n",
12804
- resolvePath: resolve$2,
12805
- useCaseSensitiveFileNames: false,
12806
- write: noop,
12807
- writeFile: noop
12808
- };
12809
- };
12810
- patchTypeScriptSysMinimum();
12811
13033
  const getTypescriptPathFromUrl = (config, tsExecutingUrl, url) => {
12812
13034
  const tsBaseUrl = new URL("..", tsExecutingUrl).href;
12813
13035
  if (url.startsWith(tsBaseUrl)) {
@@ -15251,6 +15473,22 @@ const parseStaticWatchers = (staticMembers) => {
15251
15473
  //#endregion
15252
15474
  //#region src/compiler/transformers/static-to-meta/class-extension/shared.ts
15253
15475
  /**
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.
15481
+ * @param buildCtx used to surface the warning - omit to warn silently (e.g. from tests)
15482
+ * @param targetName the identifier the target was reached through
15483
+ * @param anchor the node to attach the warning to
15484
+ */
15485
+ function warnMixinFactoryClassNotFound(buildCtx, targetName, anchor) {
15486
+ if (!buildCtx) return;
15487
+ 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.`;
15489
+ if (!buildCtx.config._isTesting) augmentDiagnosticWithNode(err, anchor);
15490
+ }
15491
+ /**
15254
15492
  * Walks the AST looking for a class declaration, optionally by name - descends
15255
15493
  * into a mixin factory's wrapping function (arrow function or `function`
15256
15494
  * declaration) body too, since a mixin factory's class is always nested one
@@ -15453,6 +15691,7 @@ function resolveAndProcessExtendedClass(compilerCtx, buildCtx, classDeclaration,
15453
15691
  if (!foundClassDeclaration && matchedStatement) {
15454
15692
  foundClassDeclaration = findClassWalk(matchedStatement);
15455
15693
  keepLooking = false;
15694
+ if (!foundClassDeclaration) warnMixinFactoryClassNotFound(buildCtx, className, classDeclaration);
15456
15695
  }
15457
15696
  if (foundClassDeclaration && !dependentClasses.some((dc) => dc.classNode === foundClassDeclaration)) {
15458
15697
  dependentClasses.push({
@@ -15508,6 +15747,7 @@ function buildExtendsTree(compilerCtx, classDeclaration, dependentClasses, typeC
15508
15747
  foundClassDeclaration = findClassWalk(node);
15509
15748
  if (!node) throw "revert to sad path";
15510
15749
  keepLooking = false;
15750
+ if (!foundClassDeclaration) warnMixinFactoryClassNotFound(buildCtx, extendee.getText(), classDeclaration);
15511
15751
  }
15512
15752
  if (foundClassDeclaration && !dependentClasses.some((dc) => dc.classNode === foundClassDeclaration)) {
15513
15753
  const foundModule = compilerCtx.moduleMap.get(foundClassDeclaration.getSourceFile().fileName);
@@ -15538,6 +15778,7 @@ function buildExtendsTree(compilerCtx, classDeclaration, dependentClasses, typeC
15538
15778
  else if (matchedStatement) {
15539
15779
  foundClassDeclaration = findClassWalk(matchedStatement);
15540
15780
  keepLooking = false;
15781
+ if (!foundClassDeclaration) warnMixinFactoryClassNotFound(buildCtx, extendee.getText(), classDeclaration);
15541
15782
  } else {
15542
15783
  foundClassDeclaration = findClassWalk(currentSource, extendee.getText());
15543
15784
  keepLooking = false;
@@ -15971,6 +16212,7 @@ function resolveAncestors(classNode, sf, path, resolveImport, visited, ancestors
15971
16212
  else {
15972
16213
  foundClass = findClassWalk(sameFileStatement);
15973
16214
  keepLooking = false;
16215
+ if (!foundClass) warnMixinFactoryClassNotFound(buildCtx, parentName, rootClassDeclaration);
15974
16216
  }
15975
16217
  } else {
15976
16218
  const specifier = findImportSpecifier(sf, parentName);
@@ -15998,6 +16240,7 @@ function resolveAncestors(classNode, sf, path, resolveImport, visited, ancestors
15998
16240
  else {
15999
16241
  foundClass = findClassWalk(found.statement);
16000
16242
  keepLooking = false;
16243
+ if (!foundClass) warnMixinFactoryClassNotFound(buildCtx, parentName, rootClassDeclaration);
16001
16244
  }
16002
16245
  }
16003
16246
  if (!foundClass || ancestors.some((a) => a.classNode === foundClass)) continue;
@@ -16727,7 +16970,7 @@ const parseCollectionComponents = (config, compilerCtx, buildCtx, collectionDir,
16727
16970
  };
16728
16971
  const transpileCollectionModule = (config, compilerCtx, buildCtx, collection, inputFileName) => {
16729
16972
  const sourceText = compilerCtx.fs.readFileSync(inputFileName);
16730
- const sourceFile = ts.createSourceFile(inputFileName, sourceText, ts.ScriptTarget.ES2017, true, ts.ScriptKind.JS);
16973
+ const sourceFile = ts.createSourceFile(inputFileName, sourceText, ts.ScriptTarget.ES2022, true, ts.ScriptKind.JS);
16731
16974
  return updateModule(config, compilerCtx, buildCtx, sourceFile, sourceText, inputFileName, void 0, collection);
16732
16975
  };
16733
16976
  //#endregion
@@ -20389,6 +20632,7 @@ const validateConfig = (userConfig = {}, bootstrapConfig) => {
20389
20632
  logger,
20390
20633
  minifyCss: config.minifyCss ?? !devMode,
20391
20634
  minifyJs: config.minifyJs ?? !devMode,
20635
+ jsMinifier: config.jsMinifier ?? "oxc",
20392
20636
  outputTargets: config.outputTargets ?? [],
20393
20637
  rolldownConfig: validateRolldownConfig(config),
20394
20638
  sourceMap: config.sourceMap === true || devMode && (config.sourceMap === "dev" || typeof config.sourceMap === "undefined"),
@@ -22897,29 +23141,36 @@ const generateTransformCssToEsm = (input, results) => {
22897
23141
  results.styleText = results.styleText.replace(/\\/g, "\\\\").replace(/\n/g, " ").replace(/\r/g, " ").replace(/\t/g, " ").replace(/\u000c/g, "\\\\f").replace(/\u0008/g, "\\\\b").replace(/\u000b/g, "\\\\v").replace(/\0/g, "\\\\0").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
22898
23142
  if (input.addTagTransformers) results.styleText = addTagTransformToCssString(results.styleText, input.tags);
22899
23143
  results.styleText = stripCssComments(results.styleText);
23144
+ const isComponentStyle = isString$1(input.tag);
23145
+ const defaultAssignment = `const ${results.defaultVarName} = ${isComponentStyle ? "() => " : ""}`;
22900
23146
  if (input.module === "cjs") {
22901
23147
  if (input.addTagTransformers) s.append(`const ${TRANSFORM_TAG} = require('${STENCIL_CORE_ID}').transformTag;\n`);
23148
+ if (!isComponentStyle) s.append(`const ${INJECT_SIDE_EFFECT_STYLE} = require('${STENCIL_CORE_ID}').injectSideEffectStyle;\n`);
22902
23149
  results.imports.forEach((cssImport) => {
22903
23150
  s.append(`const ${cssImport.varName} = require('${cssImport.importPath}');\n`);
22904
23151
  });
22905
- s.append(`const ${results.defaultVarName} = () => `);
23152
+ s.append(defaultAssignment);
22906
23153
  results.imports.forEach((cssImport) => {
22907
23154
  s.append(`${cssImport.varName} + `);
22908
23155
  });
22909
23156
  s.append(`\`${results.styleText}\`;\n`);
23157
+ if (!isComponentStyle) s.append(`${INJECT_SIDE_EFFECT_STYLE}(${results.defaultVarName});\n`);
22910
23158
  s.append(`module.exports = ${results.defaultVarName};`);
22911
23159
  } else {
22912
23160
  if (input.addTagTransformers) s.append(`import { transformTag as ${TRANSFORM_TAG} } from '${STENCIL_CORE_ID}';\n`);
23161
+ if (!isComponentStyle) s.append(`import { injectSideEffectStyle as ${INJECT_SIDE_EFFECT_STYLE} } from '${STENCIL_CORE_ID}';\n`);
22913
23162
  results.imports.forEach((cssImport) => {
22914
23163
  s.append(`import ${cssImport.varName} from '${cssImport.importPath}';\n`);
22915
23164
  });
22916
- s.append(`const ${results.defaultVarName} = () => `);
23165
+ s.append(defaultAssignment);
22917
23166
  results.imports.forEach((cssImport) => {
22918
23167
  s.append(`${cssImport.varName} + `);
22919
23168
  });
22920
23169
  s.append(`\`${results.styleText}\`;\n`);
23170
+ if (!isComponentStyle) s.append(`${INJECT_SIDE_EFFECT_STYLE}(${results.defaultVarName});\n`);
22921
23171
  s.append(`export default ${results.defaultVarName};`);
22922
23172
  }
23173
+ results.moduleSideEffects = !isComponentStyle;
22923
23174
  results.output = s.toString();
22924
23175
  return results;
22925
23176
  };
@@ -22947,6 +23198,7 @@ const getCssToEsmImports = (varNames, cssText, filePath, modeName) => {
22947
23198
  isNodeModule: false
22948
23199
  };
22949
23200
  if (!isLocalCssImport(cssImportData.srcImportText)) continue;
23201
+ else if (cssImportData.url === "stencil-globals" || cssImportData.url === "stencil-hydrate") continue;
22950
23202
  else if (isCssNodeModule(cssImportData.url)) {
22951
23203
  cssImportData.filePath = cssImportData.url.substring(1);
22952
23204
  cssImportData.isNodeModule = true;
@@ -24458,8 +24710,8 @@ const transpileModule = (config, input, transformOpts) => {
24458
24710
  getDefaultLibFileName: () => `lib.d.ts`,
24459
24711
  useCaseSensitiveFileNames: () => false,
24460
24712
  getCanonicalFileName: (fileName) => fileName,
24461
- getCurrentDirectory: () => transformOpts.currentDirectory || process.cwd(),
24462
- getNewLine: () => ts.sys.newLine || "\n",
24713
+ getCurrentDirectory: () => transformOpts.currentDirectory || (typeof process !== "undefined" ? process.cwd() : "/"),
24714
+ getNewLine: () => ts.sys && ts.sys.newLine || "\n",
24463
24715
  fileExists: (fileName) => normalizePath(fileName) === normalizePath(sourceFilePath),
24464
24716
  readFile: () => "",
24465
24717
  directoryExists: () => true,