@stencil/cli 5.0.0-alpha.4 → 5.0.0-alpha.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +5 -5
- package/dist/index.mjs +1026 -25
- package/package.json +7 -7
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as d from "@stencil/core/compiler";
|
|
2
2
|
import { LogLevel } from "@stencil/core/compiler";
|
|
3
3
|
|
|
4
4
|
//#region src/types.d.ts
|
|
@@ -11,7 +11,7 @@ type TaskCommand = 'build' | 'docs' | 'generate' | 'g' | 'help' | 'info' | 'migr
|
|
|
11
11
|
/**
|
|
12
12
|
* All the Boolean options supported by the Stencil CLI
|
|
13
13
|
*/
|
|
14
|
-
declare const BOOLEAN_CLI_FLAGS: readonly ["build", "cache", "checkVersion", "ci", "compare", "debug", "dev", "devtools", "docs", "dryRun", "
|
|
14
|
+
declare const BOOLEAN_CLI_FLAGS: readonly ["build", "cache", "checkVersion", "ci", "compare", "debug", "dev", "devtools", "docs", "dryRun", "help", "log", "open", "prerender", "prerenderExternal", "profile", "serviceWorker", "serve", "skipNodeCheck", "ssr", "verbose", "version", "watch"];
|
|
15
15
|
/**
|
|
16
16
|
* All the Number options supported by the Stencil CLI
|
|
17
17
|
*/
|
|
@@ -123,7 +123,7 @@ declare const createConfigFlags: (init?: Partial<ConfigFlags>) => ConfigFlags;
|
|
|
123
123
|
declare const parseFlags: (args: string[]) => ConfigFlags;
|
|
124
124
|
//#endregion
|
|
125
125
|
//#region src/load-compiler.d.ts
|
|
126
|
-
type CoreCompiler = typeof
|
|
126
|
+
type CoreCompiler = typeof import('@stencil/core/compiler');
|
|
127
127
|
//#endregion
|
|
128
128
|
//#region src/run.d.ts
|
|
129
129
|
/**
|
|
@@ -136,7 +136,7 @@ type CoreCompiler = typeof _$_stencil_core_compiler0;
|
|
|
136
136
|
* @param init initial CLI options
|
|
137
137
|
* @returns an empty promise
|
|
138
138
|
*/
|
|
139
|
-
declare const run: (init:
|
|
139
|
+
declare const run: (init: d.CliInitOptions) => Promise<any>;
|
|
140
140
|
/**
|
|
141
141
|
* Run a specified task
|
|
142
142
|
*
|
|
@@ -148,6 +148,6 @@ declare const run: (init: _$_stencil_core_compiler0.CliInitOptions) => Promise<a
|
|
|
148
148
|
* @public
|
|
149
149
|
* @returns a void promise
|
|
150
150
|
*/
|
|
151
|
-
declare const runTask: (coreCompiler: CoreCompiler, config:
|
|
151
|
+
declare const runTask: (coreCompiler: CoreCompiler, config: d.Config, task: TaskCommand, sys: d.CompilerSystem, flags?: ConfigFlags) => Promise<void>;
|
|
152
152
|
//#endregion
|
|
153
153
|
export { BOOLEAN_CLI_FLAGS, type ConfigFlags, type TaskCommand, createConfigFlags, parseFlags, run, runTask };
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { LOG_LEVELS } from "@stencil/core/compiler";
|
|
2
|
-
import { buildError, catchError, hasError, isFunction, isOutputTargetDocs,
|
|
3
|
-
import { isAbsolute, join, parse, relative } from "path";
|
|
2
|
+
import { buildError, catchError, hasError, isFunction, isOutputTargetDocs, isOutputTargetSsr, isOutputTargetWww, isString, normalizePath, readOnlyArrayHasStringMember, result, shouldIgnoreError, toCamelCase, validateComponentTag } from "@stencil/core/compiler/utils";
|
|
3
|
+
import { dirname, isAbsolute, join, parse, relative } from "path";
|
|
4
4
|
import ts from "typescript";
|
|
5
5
|
import { start } from "@stencil/dev-server";
|
|
6
6
|
//#region src/config-flags.ts
|
|
@@ -18,13 +18,11 @@ const BOOLEAN_CLI_FLAGS = [
|
|
|
18
18
|
"devtools",
|
|
19
19
|
"docs",
|
|
20
20
|
"dryRun",
|
|
21
|
-
"esm",
|
|
22
21
|
"help",
|
|
23
22
|
"log",
|
|
24
23
|
"open",
|
|
25
24
|
"prerender",
|
|
26
25
|
"prerenderExternal",
|
|
27
|
-
"prod",
|
|
28
26
|
"profile",
|
|
29
27
|
"serviceWorker",
|
|
30
28
|
"serve",
|
|
@@ -561,7 +559,7 @@ const startupCompilerLog = (coreCompiler, config) => {
|
|
|
561
559
|
*
|
|
562
560
|
* This function applies command-line flags to the config, with CLI flags
|
|
563
561
|
* taking precedence over config file values. This is the canonical place
|
|
564
|
-
* where flag values are translated into config properties
|
|
562
|
+
* where flag values are translated into config properties.
|
|
565
563
|
*
|
|
566
564
|
* @param config The config object (from stencil.config.ts or empty)
|
|
567
565
|
* @param flags The parsed CLI flags
|
|
@@ -569,13 +567,11 @@ const startupCompilerLog = (coreCompiler, config) => {
|
|
|
569
567
|
*/
|
|
570
568
|
const mergeFlags = (config, flags) => {
|
|
571
569
|
const merged = { ...config };
|
|
572
|
-
if (flags.
|
|
573
|
-
else if (flags.dev === true) merged.devMode = true;
|
|
570
|
+
if (flags.dev === true) merged.devMode = true;
|
|
574
571
|
if (flags.debug === true || flags.verbose === true) merged.logLevel = "debug";
|
|
575
572
|
else if (flags.logLevel) merged.logLevel = flags.logLevel;
|
|
576
573
|
if (typeof flags.watch === "boolean") merged.watch = flags.watch;
|
|
577
|
-
if (
|
|
578
|
-
if (typeof flags.esm === "boolean") merged.buildDist = flags.esm;
|
|
574
|
+
if (flags.docs === true) merged._docsFlag = true;
|
|
579
575
|
if (typeof flags.profile === "boolean") merged.profile = flags.profile;
|
|
580
576
|
if (typeof flags.log === "boolean") merged.writeLog = flags.log;
|
|
581
577
|
if (typeof flags.cache === "boolean") merged.enableCache = flags.cache;
|
|
@@ -618,6 +614,164 @@ const printCheckVersionResults = async (versionChecker) => {
|
|
|
618
614
|
}
|
|
619
615
|
};
|
|
620
616
|
//#endregion
|
|
617
|
+
//#region src/migrations/rules/build-dist-docs.ts
|
|
618
|
+
/**
|
|
619
|
+
* Migration rule for buildDist and buildDocs removal.
|
|
620
|
+
*
|
|
621
|
+
* In Stencil v5, these global config options are replaced with per-output-target
|
|
622
|
+
* `skipInDev` property. This migration:
|
|
623
|
+
* - Detects `buildDist` or `buildDocs` in stencil.config.ts
|
|
624
|
+
* - Removes the property
|
|
625
|
+
* - For `buildDist: true` or `buildDocs: true`, adds `skipInDev: false` to relevant output targets
|
|
626
|
+
*/
|
|
627
|
+
const buildDistDocsRule = {
|
|
628
|
+
id: "build-dist-docs",
|
|
629
|
+
name: "buildDist/buildDocs Removal",
|
|
630
|
+
description: "Remove deprecated buildDist and buildDocs config options",
|
|
631
|
+
fromVersion: "4.x",
|
|
632
|
+
toVersion: "5.x",
|
|
633
|
+
detect(sourceFile) {
|
|
634
|
+
const matches = [];
|
|
635
|
+
const visit = (node) => {
|
|
636
|
+
if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.name)) {
|
|
637
|
+
const propName = node.name.text;
|
|
638
|
+
if (propName === "buildDist" || propName === "buildDocs") {
|
|
639
|
+
const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
|
|
640
|
+
const isTrue = node.initializer.kind === ts.SyntaxKind.TrueKeyword;
|
|
641
|
+
matches.push({
|
|
642
|
+
node,
|
|
643
|
+
message: `Deprecated '${propName}' found${isTrue ? " (set to true)" : ""} - will be removed and skipInDev added to output targets`,
|
|
644
|
+
line: line + 1,
|
|
645
|
+
column: character + 1
|
|
646
|
+
});
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
ts.forEachChild(node, visit);
|
|
650
|
+
};
|
|
651
|
+
visit(sourceFile);
|
|
652
|
+
return matches;
|
|
653
|
+
},
|
|
654
|
+
transform(sourceFile, matches) {
|
|
655
|
+
if (matches.length === 0) return sourceFile.getFullText();
|
|
656
|
+
let text = sourceFile.getFullText();
|
|
657
|
+
const targetTypesNeedingSkipInDev = [];
|
|
658
|
+
for (const match of matches) {
|
|
659
|
+
const prop = match.node;
|
|
660
|
+
const propName = prop.name.text;
|
|
661
|
+
if (prop.initializer.kind === ts.SyntaxKind.TrueKeyword) {
|
|
662
|
+
if (propName === "buildDist") targetTypesNeedingSkipInDev.push("dist", "dist-custom-elements", "dist-hydrate-script", "loader-bundle", "standalone", "ssr");
|
|
663
|
+
else if (propName === "buildDocs") targetTypesNeedingSkipInDev.push("docs-readme", "docs-json", "docs-custom", "docs-vscode", "docs-custom-elements-manifest");
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
const outputTargetsToModify = [];
|
|
667
|
+
const findOutputTargets = (node) => {
|
|
668
|
+
if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.name) && node.name.text === "outputTargets" && ts.isArrayLiteralExpression(node.initializer)) {
|
|
669
|
+
for (const element of node.initializer.elements) if (ts.isObjectLiteralExpression(element)) {
|
|
670
|
+
const typeProp = element.properties.find((p) => ts.isPropertyAssignment(p) && ts.isIdentifier(p.name) && p.name.text === "type");
|
|
671
|
+
if (typeProp && ts.isStringLiteral(typeProp.initializer)) {
|
|
672
|
+
const targetType = typeProp.initializer.text;
|
|
673
|
+
if (targetTypesNeedingSkipInDev.includes(targetType)) {
|
|
674
|
+
if (!element.properties.some((p) => ts.isPropertyAssignment(p) && ts.isIdentifier(p.name) && p.name.text === "skipInDev")) outputTargetsToModify.push(element);
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
ts.forEachChild(node, findOutputTargets);
|
|
680
|
+
};
|
|
681
|
+
findOutputTargets(sourceFile);
|
|
682
|
+
const allModifications = [];
|
|
683
|
+
for (const match of matches) {
|
|
684
|
+
const prop = match.node;
|
|
685
|
+
const start = prop.getStart();
|
|
686
|
+
let end = prop.getEnd();
|
|
687
|
+
const afterProp = text.slice(end).match(/^\s*,/);
|
|
688
|
+
if (afterProp) end = end + afterProp[0].length;
|
|
689
|
+
allModifications.push({
|
|
690
|
+
type: "remove",
|
|
691
|
+
start,
|
|
692
|
+
end,
|
|
693
|
+
node: prop
|
|
694
|
+
});
|
|
695
|
+
}
|
|
696
|
+
for (const target of outputTargetsToModify) {
|
|
697
|
+
const lastProp = target.properties[target.properties.length - 1];
|
|
698
|
+
if (lastProp) allModifications.push({
|
|
699
|
+
type: "addSkipInDev",
|
|
700
|
+
start: lastProp.getEnd(),
|
|
701
|
+
end: lastProp.getEnd(),
|
|
702
|
+
node: target
|
|
703
|
+
});
|
|
704
|
+
}
|
|
705
|
+
allModifications.sort((a, b) => b.start - a.start);
|
|
706
|
+
for (const mod of allModifications) if (mod.type === "remove") text = text.slice(0, mod.start) + text.slice(mod.end);
|
|
707
|
+
else if (mod.type === "addSkipInDev") {
|
|
708
|
+
const firstProp = mod.node.properties[0];
|
|
709
|
+
let indent = " ";
|
|
710
|
+
if (firstProp) {
|
|
711
|
+
const propStart = firstProp.getStart();
|
|
712
|
+
const lineStart = text.lastIndexOf("\n", propStart) + 1;
|
|
713
|
+
const leadingWhitespace = text.slice(lineStart, propStart);
|
|
714
|
+
if (/^\s+$/.test(leadingWhitespace)) indent = leadingWhitespace;
|
|
715
|
+
}
|
|
716
|
+
const afterLastProp = text.slice(mod.start).match(/^(\s*,)?/);
|
|
717
|
+
const skipLength = afterLastProp && afterLastProp[1] ? afterLastProp[0].length : 0;
|
|
718
|
+
text = text.slice(0, mod.start) + `,\n${indent}skipInDev: false,` + text.slice(mod.start + skipLength);
|
|
719
|
+
}
|
|
720
|
+
text = text.replace(/,\s*\n\s*\n/g, ",\n");
|
|
721
|
+
text = text.replace(/{\s*\n\s*\n/g, "{\n");
|
|
722
|
+
return text;
|
|
723
|
+
}
|
|
724
|
+
};
|
|
725
|
+
//#endregion
|
|
726
|
+
//#region src/migrations/rules/dev-mode.ts
|
|
727
|
+
/**
|
|
728
|
+
* Migration rule for `devMode` config option removal.
|
|
729
|
+
*
|
|
730
|
+
* In Stencil v5, `devMode` is no longer a user-settable config option in `stencil.config.ts`.
|
|
731
|
+
* Build mode is controlled exclusively by the `--dev` CLI flag (default is production).
|
|
732
|
+
* This migration removes `devMode` from the config object.
|
|
733
|
+
*/
|
|
734
|
+
const devModeRule = {
|
|
735
|
+
id: "dev-mode",
|
|
736
|
+
name: "devMode Config Removal",
|
|
737
|
+
description: "Remove deprecated devMode config option - use --dev CLI flag instead",
|
|
738
|
+
fromVersion: "4.x",
|
|
739
|
+
toVersion: "5.x",
|
|
740
|
+
detect(sourceFile) {
|
|
741
|
+
const matches = [];
|
|
742
|
+
const visit = (node) => {
|
|
743
|
+
if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.name)) {
|
|
744
|
+
if (node.name.text === "devMode") {
|
|
745
|
+
const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
|
|
746
|
+
matches.push({
|
|
747
|
+
node,
|
|
748
|
+
message: `Deprecated 'devMode' config option found - build mode is now set via the --dev CLI flag (production is the default)`,
|
|
749
|
+
line: line + 1,
|
|
750
|
+
column: character + 1
|
|
751
|
+
});
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
ts.forEachChild(node, visit);
|
|
755
|
+
};
|
|
756
|
+
visit(sourceFile);
|
|
757
|
+
return matches;
|
|
758
|
+
},
|
|
759
|
+
transform(sourceFile, matches) {
|
|
760
|
+
if (matches.length === 0) return sourceFile.getFullText();
|
|
761
|
+
let text = sourceFile.getFullText();
|
|
762
|
+
for (const match of [...matches].reverse()) {
|
|
763
|
+
const node = match.node;
|
|
764
|
+
const start = node.getFullStart();
|
|
765
|
+
const end = node.getEnd();
|
|
766
|
+
let removeEnd = end;
|
|
767
|
+
const trailingComma = text.slice(end).match(/^\s*,/);
|
|
768
|
+
if (trailingComma) removeEnd = end + trailingComma[0].length;
|
|
769
|
+
text = text.slice(0, start) + text.slice(removeEnd);
|
|
770
|
+
}
|
|
771
|
+
return text;
|
|
772
|
+
}
|
|
773
|
+
};
|
|
774
|
+
//#endregion
|
|
621
775
|
//#region src/migrations/rules/encapsulation-api.ts
|
|
622
776
|
/**
|
|
623
777
|
* Migration rule for the @Component encapsulation API change.
|
|
@@ -699,6 +853,57 @@ const encapsulationApiRule = {
|
|
|
699
853
|
}
|
|
700
854
|
};
|
|
701
855
|
//#endregion
|
|
856
|
+
//#region src/migrations/rules/external-runtime.ts
|
|
857
|
+
/**
|
|
858
|
+
* Migration rule for `externalRuntime` on `standalone` output targets.
|
|
859
|
+
*
|
|
860
|
+
* In Stencil v5, `externalRuntime` defaults to `false` (was `true` in v4).
|
|
861
|
+
* Explicit `externalRuntime: false` is now redundant and can be removed.
|
|
862
|
+
*/
|
|
863
|
+
const externalRuntimeRule = {
|
|
864
|
+
id: "external-runtime",
|
|
865
|
+
name: "externalRuntime Default Change",
|
|
866
|
+
description: "Remove redundant 'externalRuntime: false' from standalone output targets - false is now the default",
|
|
867
|
+
fromVersion: "4.x",
|
|
868
|
+
toVersion: "5.x",
|
|
869
|
+
detect(sourceFile) {
|
|
870
|
+
const matches = [];
|
|
871
|
+
const visit = (node) => {
|
|
872
|
+
if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.name) && node.name.text === "externalRuntime" && node.initializer.kind === ts.SyntaxKind.FalseKeyword) {
|
|
873
|
+
const parent = node.parent;
|
|
874
|
+
if (ts.isObjectLiteralExpression(parent)) {
|
|
875
|
+
if (parent.properties.some((p) => ts.isPropertyAssignment(p) && ts.isIdentifier(p.name) && p.name.text === "type" && ts.isStringLiteral(p.initializer) && p.initializer.text === "standalone")) {
|
|
876
|
+
const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
|
|
877
|
+
matches.push({
|
|
878
|
+
node,
|
|
879
|
+
message: "'externalRuntime: false' is now the default - this property can be removed",
|
|
880
|
+
line: line + 1,
|
|
881
|
+
column: character + 1
|
|
882
|
+
});
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
ts.forEachChild(node, visit);
|
|
887
|
+
};
|
|
888
|
+
visit(sourceFile);
|
|
889
|
+
return matches;
|
|
890
|
+
},
|
|
891
|
+
transform(sourceFile, matches) {
|
|
892
|
+
if (matches.length === 0) return sourceFile.getFullText();
|
|
893
|
+
let text = sourceFile.getFullText();
|
|
894
|
+
for (const match of [...matches].reverse()) {
|
|
895
|
+
const node = match.node;
|
|
896
|
+
const start = node.getFullStart();
|
|
897
|
+
const end = node.getEnd();
|
|
898
|
+
let removeEnd = end;
|
|
899
|
+
const trailingComma = text.slice(end).match(/^\s*,/);
|
|
900
|
+
if (trailingComma) removeEnd = end + trailingComma[0].length;
|
|
901
|
+
text = text.slice(0, start) + text.slice(removeEnd);
|
|
902
|
+
}
|
|
903
|
+
return text;
|
|
904
|
+
}
|
|
905
|
+
};
|
|
906
|
+
//#endregion
|
|
702
907
|
//#region src/migrations/rules/form-associated.ts
|
|
703
908
|
/**
|
|
704
909
|
* Migration rule for formAssociated → @AttachInternals.
|
|
@@ -820,6 +1025,750 @@ const formAssociatedRule = {
|
|
|
820
1025
|
}
|
|
821
1026
|
};
|
|
822
1027
|
//#endregion
|
|
1028
|
+
//#region src/migrations/rules/global-style-inject.ts
|
|
1029
|
+
/**
|
|
1030
|
+
* Migration rule for `extras.addGlobalStyleToComponents` → `global-style` output target `inject`.
|
|
1031
|
+
*
|
|
1032
|
+
* In v5, the `addGlobalStyleToComponents` extras option is removed. Instead, configure
|
|
1033
|
+
* the `inject` option on the `global-style` output target.
|
|
1034
|
+
*
|
|
1035
|
+
* Migration mapping:
|
|
1036
|
+
* - `addGlobalStyleToComponents: true` → `inject: 'all'`
|
|
1037
|
+
* - `addGlobalStyleToComponents: 'client'` → `inject: 'client'`
|
|
1038
|
+
* - `addGlobalStyleToComponents: false` → omit (default is 'none')
|
|
1039
|
+
*
|
|
1040
|
+
* Note: This migration only acts on explicitly set `addGlobalStyleToComponents`.
|
|
1041
|
+
* Users who relied on the implicit v4 default ('client') will need to manually
|
|
1042
|
+
* add `inject: 'client'` to their global-style output target if desired.
|
|
1043
|
+
*/
|
|
1044
|
+
const globalStyleInjectRule = {
|
|
1045
|
+
id: "global-style-inject",
|
|
1046
|
+
name: "Global Style Inject Migration",
|
|
1047
|
+
description: "Migrate extras.addGlobalStyleToComponents to global-style output target inject option",
|
|
1048
|
+
fromVersion: "4.x",
|
|
1049
|
+
toVersion: "5.x",
|
|
1050
|
+
detect(sourceFile) {
|
|
1051
|
+
const matches = [];
|
|
1052
|
+
const visit = (node) => {
|
|
1053
|
+
if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.name) && node.name.text === "addGlobalStyleToComponents") {
|
|
1054
|
+
const parent = node.parent;
|
|
1055
|
+
if (ts.isObjectLiteralExpression(parent)) {
|
|
1056
|
+
const grandparent = parent.parent;
|
|
1057
|
+
if (ts.isPropertyAssignment(grandparent) && ts.isIdentifier(grandparent.name) && grandparent.name.text === "extras") {
|
|
1058
|
+
const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
|
|
1059
|
+
matches.push({
|
|
1060
|
+
node,
|
|
1061
|
+
message: `extras.addGlobalStyleToComponents is removed. Use 'inject' on global-style output target instead.`,
|
|
1062
|
+
line: line + 1,
|
|
1063
|
+
column: character + 1
|
|
1064
|
+
});
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
ts.forEachChild(node, visit);
|
|
1069
|
+
};
|
|
1070
|
+
visit(sourceFile);
|
|
1071
|
+
return matches;
|
|
1072
|
+
},
|
|
1073
|
+
transform(sourceFile, matches) {
|
|
1074
|
+
if (matches.length === 0) return sourceFile.getFullText();
|
|
1075
|
+
let text = sourceFile.getFullText();
|
|
1076
|
+
const matchNode = matches[0].node;
|
|
1077
|
+
if (!ts.isPropertyAssignment(matchNode)) return text;
|
|
1078
|
+
let injectValue = null;
|
|
1079
|
+
if (matchNode.initializer.kind === ts.SyntaxKind.TrueKeyword) injectValue = "all";
|
|
1080
|
+
else if (ts.isStringLiteral(matchNode.initializer) && matchNode.initializer.text === "client") injectValue = "client";
|
|
1081
|
+
let insertionOffset = 0;
|
|
1082
|
+
if (injectValue) {
|
|
1083
|
+
const result = addGlobalStyleOutputTarget(text, sourceFile, injectValue);
|
|
1084
|
+
insertionOffset = result.length - text.length;
|
|
1085
|
+
text = result;
|
|
1086
|
+
}
|
|
1087
|
+
let start = matchNode.getStart();
|
|
1088
|
+
let end = matchNode.getEnd();
|
|
1089
|
+
const outputTargetsEnd = findOutputTargetsEnd(sourceFile);
|
|
1090
|
+
if (insertionOffset !== 0 && outputTargetsEnd !== null && matchNode.getStart() > outputTargetsEnd) {
|
|
1091
|
+
start += insertionOffset;
|
|
1092
|
+
end += insertionOffset;
|
|
1093
|
+
}
|
|
1094
|
+
const afterProp = text.slice(end).match(/^\s*,/);
|
|
1095
|
+
if (afterProp) end = end + afterProp[0].length;
|
|
1096
|
+
else {
|
|
1097
|
+
const beforeProp = text.slice(0, start).match(/,\s*$/);
|
|
1098
|
+
if (beforeProp) {
|
|
1099
|
+
text = text.slice(0, start - beforeProp[0].length) + text.slice(end);
|
|
1100
|
+
end = -1;
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
if (end !== -1) text = text.slice(0, start) + text.slice(end);
|
|
1104
|
+
text = cleanupEmptyExtras$1(text);
|
|
1105
|
+
return text;
|
|
1106
|
+
}
|
|
1107
|
+
};
|
|
1108
|
+
/**
|
|
1109
|
+
* Find the end position of the outputTargets array in the source file.
|
|
1110
|
+
* @param sourceFile The source file to search
|
|
1111
|
+
* @returns The end position of the outputTargets array, or null if not found
|
|
1112
|
+
*/
|
|
1113
|
+
function findOutputTargetsEnd(sourceFile) {
|
|
1114
|
+
let endPos = null;
|
|
1115
|
+
const visit = (node) => {
|
|
1116
|
+
if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.name) && node.name.text === "outputTargets") endPos = node.getEnd();
|
|
1117
|
+
ts.forEachChild(node, visit);
|
|
1118
|
+
};
|
|
1119
|
+
visit(sourceFile);
|
|
1120
|
+
return endPos;
|
|
1121
|
+
}
|
|
1122
|
+
/**
|
|
1123
|
+
* Clean up empty extras object after removing addGlobalStyleToComponents.
|
|
1124
|
+
* @param text The source text to clean up
|
|
1125
|
+
* @returns The cleaned up text with empty extras removed
|
|
1126
|
+
*/
|
|
1127
|
+
function cleanupEmptyExtras$1(text) {
|
|
1128
|
+
return text.replace(/,?\s*extras\s*:\s*\{\s*\},?/g, "");
|
|
1129
|
+
}
|
|
1130
|
+
/**
|
|
1131
|
+
* Add a global-style output target with the specified inject value.
|
|
1132
|
+
* @param text The source text to modify
|
|
1133
|
+
* @param sourceFile The source file for position info
|
|
1134
|
+
* @param injectValue The inject value to set on the new global-style output target
|
|
1135
|
+
* @returns The modified text with the new global-style output target added
|
|
1136
|
+
*/
|
|
1137
|
+
function addGlobalStyleOutputTarget(text, sourceFile, injectValue) {
|
|
1138
|
+
if (text.includes("type: 'global-style'")) return text;
|
|
1139
|
+
let outputTargetsArray = null;
|
|
1140
|
+
const findOutputTargets = (node) => {
|
|
1141
|
+
if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.name) && node.name.text === "outputTargets" && ts.isArrayLiteralExpression(node.initializer)) outputTargetsArray = node.initializer;
|
|
1142
|
+
ts.forEachChild(node, findOutputTargets);
|
|
1143
|
+
};
|
|
1144
|
+
findOutputTargets(sourceFile);
|
|
1145
|
+
if (!outputTargetsArray) return text;
|
|
1146
|
+
const array = outputTargetsArray;
|
|
1147
|
+
let indent = " ";
|
|
1148
|
+
if (array.elements.length > 0) {
|
|
1149
|
+
const elemStart = array.elements[0].getStart();
|
|
1150
|
+
const lineStart = text.lastIndexOf("\n", elemStart) + 1;
|
|
1151
|
+
const leadingWhitespace = text.slice(lineStart, elemStart);
|
|
1152
|
+
if (/^\s+$/.test(leadingWhitespace)) indent = leadingWhitespace;
|
|
1153
|
+
}
|
|
1154
|
+
const lastElement = array.elements[array.elements.length - 1];
|
|
1155
|
+
if (!lastElement) {
|
|
1156
|
+
const arrayStart = array.getStart() + 1;
|
|
1157
|
+
const newTarget = `\n${indent}{\n${indent} type: 'global-style',\n${indent} inject: '${injectValue}',\n${indent}}\n${indent.slice(2)}`;
|
|
1158
|
+
return text.slice(0, arrayStart) + newTarget + text.slice(arrayStart);
|
|
1159
|
+
}
|
|
1160
|
+
let insertPos = lastElement.getEnd();
|
|
1161
|
+
const afterLastElement = text.slice(insertPos).match(/^(\s*,)?/);
|
|
1162
|
+
const hasTrailingComma = afterLastElement && afterLastElement[1];
|
|
1163
|
+
if (hasTrailingComma) insertPos += afterLastElement[0].length;
|
|
1164
|
+
const newTarget = `{\n${indent} type: 'global-style',\n${indent} inject: '${injectValue}',\n${indent}}`;
|
|
1165
|
+
const insertion = (hasTrailingComma ? "" : ",") + "\n" + indent + newTarget;
|
|
1166
|
+
return text.slice(0, insertPos) + insertion + text.slice(insertPos);
|
|
1167
|
+
}
|
|
1168
|
+
//#endregion
|
|
1169
|
+
//#region src/migrations/rules/hash-file-names.ts
|
|
1170
|
+
/**
|
|
1171
|
+
* Migration rule for `hashFileNames` and `hashedFileNameLength` config options.
|
|
1172
|
+
*
|
|
1173
|
+
* In Stencil v5, these are no longer top-level config options. They belong on
|
|
1174
|
+
* the `loader-bundle` and `www` output targets, which are the only outputs that
|
|
1175
|
+
* are served directly in the browser and benefit from content-hash caching.
|
|
1176
|
+
*
|
|
1177
|
+
* This migration:
|
|
1178
|
+
* 1. Removes them from the top-level config
|
|
1179
|
+
* 2. Injects them into any `loader-bundle` and `www` output targets found in the same file
|
|
1180
|
+
*/
|
|
1181
|
+
const hashFileNamesRule = {
|
|
1182
|
+
id: "hash-file-names",
|
|
1183
|
+
name: "Hash File Names Config Move",
|
|
1184
|
+
description: "Move hashFileNames and hashedFileNameLength from top-level config to loader-bundle and www output targets",
|
|
1185
|
+
fromVersion: "4.x",
|
|
1186
|
+
toVersion: "5.x",
|
|
1187
|
+
detect(sourceFile) {
|
|
1188
|
+
const matches = [];
|
|
1189
|
+
const visit = (node) => {
|
|
1190
|
+
if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.name) && (node.name.text === "hashFileNames" || node.name.text === "hashedFileNameLength")) {
|
|
1191
|
+
const parent = node.parent;
|
|
1192
|
+
if (ts.isObjectLiteralExpression(parent)) {
|
|
1193
|
+
if (!parent.properties.some((p) => ts.isPropertyAssignment(p) && ts.isIdentifier(p.name) && p.name.text === "type")) {
|
|
1194
|
+
const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
|
|
1195
|
+
matches.push({
|
|
1196
|
+
node,
|
|
1197
|
+
message: `'${node.name.text}' is no longer a top-level config option. Move it to your 'loader-bundle' and/or 'www' output targets.`,
|
|
1198
|
+
line: line + 1,
|
|
1199
|
+
column: character + 1
|
|
1200
|
+
});
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
ts.forEachChild(node, visit);
|
|
1205
|
+
};
|
|
1206
|
+
visit(sourceFile);
|
|
1207
|
+
return matches;
|
|
1208
|
+
},
|
|
1209
|
+
transform(sourceFile, matches) {
|
|
1210
|
+
if (matches.length === 0) return sourceFile.getFullText();
|
|
1211
|
+
const fullText = sourceFile.getFullText();
|
|
1212
|
+
const propsToInject = matches.map((m) => {
|
|
1213
|
+
const node = m.node;
|
|
1214
|
+
return {
|
|
1215
|
+
name: node.name.text,
|
|
1216
|
+
value: node.initializer.getText(sourceFile)
|
|
1217
|
+
};
|
|
1218
|
+
});
|
|
1219
|
+
const TARGET_TYPES = new Set(["loader-bundle", "www"]);
|
|
1220
|
+
const insertionsByPos = /* @__PURE__ */ new Map();
|
|
1221
|
+
const visit = (node) => {
|
|
1222
|
+
if (ts.isObjectLiteralExpression(node)) {
|
|
1223
|
+
if (node.properties.find((p) => ts.isPropertyAssignment(p) && ts.isIdentifier(p.name) && p.name.text === "type" && ts.isStringLiteral(p.initializer) && TARGET_TYPES.has(p.initializer.text))) {
|
|
1224
|
+
const existingPropNames = new Set(node.properties.filter((p) => ts.isPropertyAssignment(p) && ts.isIdentifier(p.name)).map((p) => p.name.text));
|
|
1225
|
+
const objStart = node.getStart(sourceFile);
|
|
1226
|
+
const objLineStart = fullText.lastIndexOf("\n", objStart) + 1;
|
|
1227
|
+
const propIndent = (fullText.slice(objLineStart, objStart).match(/^(\s*)/)?.[1] ?? "") + " ";
|
|
1228
|
+
const closingBracePos = node.getEnd() - 1;
|
|
1229
|
+
const isMultiLine = fullText.slice(objStart, node.getEnd()).includes("\n");
|
|
1230
|
+
let insertPos;
|
|
1231
|
+
if (isMultiLine) insertPos = fullText.lastIndexOf("\n", closingBracePos - 1);
|
|
1232
|
+
else {
|
|
1233
|
+
insertPos = closingBracePos;
|
|
1234
|
+
while (insertPos > 0 && fullText[insertPos - 1] === " ") insertPos--;
|
|
1235
|
+
}
|
|
1236
|
+
const existing = insertionsByPos.get(insertPos) ?? [];
|
|
1237
|
+
for (const prop of propsToInject) if (!existingPropNames.has(prop.name)) existing.push(isMultiLine ? `\n${propIndent}${prop.name}: ${prop.value},` : `, ${prop.name}: ${prop.value}`);
|
|
1238
|
+
if (existing.length > 0) insertionsByPos.set(insertPos, existing);
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
ts.forEachChild(node, visit);
|
|
1242
|
+
};
|
|
1243
|
+
visit(sourceFile);
|
|
1244
|
+
const edits = [];
|
|
1245
|
+
for (const match of matches) {
|
|
1246
|
+
const node = match.node;
|
|
1247
|
+
const start = node.getFullStart();
|
|
1248
|
+
const end = node.getEnd();
|
|
1249
|
+
let removeEnd = end;
|
|
1250
|
+
const trailingComma = fullText.slice(end).match(/^\s*,/);
|
|
1251
|
+
if (trailingComma) removeEnd = end + trailingComma[0].length;
|
|
1252
|
+
edits.push({
|
|
1253
|
+
start,
|
|
1254
|
+
end: removeEnd,
|
|
1255
|
+
replacement: ""
|
|
1256
|
+
});
|
|
1257
|
+
}
|
|
1258
|
+
for (const [pos, insertions] of insertionsByPos) edits.push({
|
|
1259
|
+
start: pos,
|
|
1260
|
+
end: pos,
|
|
1261
|
+
replacement: insertions.join("")
|
|
1262
|
+
});
|
|
1263
|
+
edits.sort((a, b) => b.start - a.start);
|
|
1264
|
+
let text = fullText;
|
|
1265
|
+
for (const edit of edits) text = text.slice(0, edit.start) + edit.replacement + text.slice(edit.end);
|
|
1266
|
+
return text;
|
|
1267
|
+
}
|
|
1268
|
+
};
|
|
1269
|
+
//#endregion
|
|
1270
|
+
//#region src/migrations/rules/light-dom-patches.ts
|
|
1271
|
+
/**
|
|
1272
|
+
* Migration rule for slot-fix extras → `lightDomPatches`.
|
|
1273
|
+
*
|
|
1274
|
+
* In v5, the individual slot-fix flags and `experimentalSlotFixes` umbrella are replaced
|
|
1275
|
+
* by a single `lightDomPatches` option which defaults to `true`.
|
|
1276
|
+
*
|
|
1277
|
+
* Migration mapping:
|
|
1278
|
+
* - `experimentalSlotFixes: true` → remove (new default is `true`)
|
|
1279
|
+
* - `experimentalSlotFixes: false` → `lightDomPatches: false`
|
|
1280
|
+
* - Individual flags only → `lightDomPatches: { <new names> }`
|
|
1281
|
+
*
|
|
1282
|
+
* Old → new individual key names:
|
|
1283
|
+
* appendChildSlotFix → domMutations
|
|
1284
|
+
* cloneNodeFix → cloneNode
|
|
1285
|
+
* scopedSlotTextContentFix → textContent
|
|
1286
|
+
* slotChildNodesFix → childNodes
|
|
1287
|
+
*/
|
|
1288
|
+
const lightDomPatchesRule = {
|
|
1289
|
+
id: "light-dom-patches",
|
|
1290
|
+
name: "Light DOM Patches Migration",
|
|
1291
|
+
description: "Migrate experimentalSlotFixes / individual slot-fix flags to lightDomPatches",
|
|
1292
|
+
fromVersion: "4.x",
|
|
1293
|
+
toVersion: "5.x",
|
|
1294
|
+
detect(sourceFile) {
|
|
1295
|
+
const matches = [];
|
|
1296
|
+
const oldKeys = new Set([
|
|
1297
|
+
"experimentalSlotFixes",
|
|
1298
|
+
"appendChildSlotFix",
|
|
1299
|
+
"cloneNodeFix",
|
|
1300
|
+
"scopedSlotTextContentFix",
|
|
1301
|
+
"slotChildNodesFix"
|
|
1302
|
+
]);
|
|
1303
|
+
const visit = (node) => {
|
|
1304
|
+
if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.name) && oldKeys.has(node.name.text)) {
|
|
1305
|
+
const parent = node.parent;
|
|
1306
|
+
if (ts.isObjectLiteralExpression(parent)) {
|
|
1307
|
+
const grandparent = parent.parent;
|
|
1308
|
+
if (ts.isPropertyAssignment(grandparent) && ts.isIdentifier(grandparent.name) && grandparent.name.text === "extras") {
|
|
1309
|
+
const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
|
|
1310
|
+
matches.push({
|
|
1311
|
+
node,
|
|
1312
|
+
message: `extras.${node.name.text} is removed. Use 'extras.lightDomPatches' instead.`,
|
|
1313
|
+
line: line + 1,
|
|
1314
|
+
column: character + 1
|
|
1315
|
+
});
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
ts.forEachChild(node, visit);
|
|
1320
|
+
};
|
|
1321
|
+
visit(sourceFile);
|
|
1322
|
+
return matches;
|
|
1323
|
+
},
|
|
1324
|
+
transform(sourceFile, matches) {
|
|
1325
|
+
if (matches.length === 0) return sourceFile.getFullText();
|
|
1326
|
+
const oldKeyMap = {};
|
|
1327
|
+
for (const match of matches) {
|
|
1328
|
+
const node = match.node;
|
|
1329
|
+
const key = node.name.text;
|
|
1330
|
+
const init = node.initializer;
|
|
1331
|
+
oldKeyMap[key] = init.kind === ts.SyntaxKind.TrueKeyword ? true : init.kind === ts.SyntaxKind.FalseKeyword ? false : void 0;
|
|
1332
|
+
}
|
|
1333
|
+
const keyRename = {
|
|
1334
|
+
appendChildSlotFix: "domMutations",
|
|
1335
|
+
cloneNodeFix: "cloneNode",
|
|
1336
|
+
scopedSlotTextContentFix: "textContent",
|
|
1337
|
+
slotChildNodesFix: "childNodes"
|
|
1338
|
+
};
|
|
1339
|
+
const experimentalValue = oldKeyMap["experimentalSlotFixes"];
|
|
1340
|
+
const hasIndividualKeys = matches.some((m) => {
|
|
1341
|
+
return m.node.name.text !== "experimentalSlotFixes";
|
|
1342
|
+
});
|
|
1343
|
+
let replacement = null;
|
|
1344
|
+
if (experimentalValue === true && !hasIndividualKeys) replacement = null;
|
|
1345
|
+
else if (experimentalValue === false && !hasIndividualKeys) replacement = "lightDomPatches: false";
|
|
1346
|
+
else if (experimentalValue === true && hasIndividualKeys) replacement = null;
|
|
1347
|
+
else {
|
|
1348
|
+
const parts = [];
|
|
1349
|
+
for (const [oldKey, newKey] of Object.entries(keyRename)) {
|
|
1350
|
+
const val = oldKeyMap[oldKey];
|
|
1351
|
+
if (val === true) parts.push(`${newKey}: true`);
|
|
1352
|
+
else if (val === false) parts.push(`${newKey}: false`);
|
|
1353
|
+
}
|
|
1354
|
+
if (parts.length > 0) replacement = `lightDomPatches: { ${parts.join(", ")} }`;
|
|
1355
|
+
}
|
|
1356
|
+
const sorted = [...matches].sort((a, b) => b.node.getStart() - a.node.getStart());
|
|
1357
|
+
let text = sourceFile.getFullText();
|
|
1358
|
+
let replacementInserted = false;
|
|
1359
|
+
for (const match of sorted) {
|
|
1360
|
+
const node = match.node;
|
|
1361
|
+
let start = node.getStart();
|
|
1362
|
+
let end = node.getEnd();
|
|
1363
|
+
const trailingComma = text.slice(end).match(/^(\s*,)/);
|
|
1364
|
+
if (trailingComma) end += trailingComma[1].length;
|
|
1365
|
+
else {
|
|
1366
|
+
const leadingComma = text.slice(0, start).match(/,\s*$/);
|
|
1367
|
+
if (leadingComma) start -= leadingComma[0].length;
|
|
1368
|
+
}
|
|
1369
|
+
if (!replacementInserted && replacement !== null) {
|
|
1370
|
+
text = text.slice(0, start) + replacement + text.slice(end);
|
|
1371
|
+
replacementInserted = true;
|
|
1372
|
+
} else text = text.slice(0, start) + text.slice(end);
|
|
1373
|
+
}
|
|
1374
|
+
text = cleanupEmptyExtras(text);
|
|
1375
|
+
return text;
|
|
1376
|
+
}
|
|
1377
|
+
};
|
|
1378
|
+
function cleanupEmptyExtras(text) {
|
|
1379
|
+
return text.replace(/,?\s*extras\s*:\s*\{\s*\},?/g, "");
|
|
1380
|
+
}
|
|
1381
|
+
//#endregion
|
|
1382
|
+
//#region src/migrations/rules/output-target-renames.ts
|
|
1383
|
+
/**
|
|
1384
|
+
* Migration rule for output target renames in Stencil v5.
|
|
1385
|
+
*
|
|
1386
|
+
* This migration:
|
|
1387
|
+
* - Renames `dist` → `loader-bundle`
|
|
1388
|
+
* - Renames `dist-custom-elements` → `standalone`
|
|
1389
|
+
* - Renames `dist-hydrate-script` → `ssr`
|
|
1390
|
+
* - Renames `dist-collection` → `collection`
|
|
1391
|
+
* - Renames `dist-types` → `types`
|
|
1392
|
+
* - Extracts `collectionDir` from loader-bundle into separate `collection` output
|
|
1393
|
+
* - Extracts `typesDir` from loader-bundle into separate `types` output
|
|
1394
|
+
* - Renames `esmLoaderPath` → `loaderPath` (applies to all module formats, not just ESM)
|
|
1395
|
+
* - Removes `isPrimaryPackageOutputTarget` (no longer needed, package.json validation auto-detects)
|
|
1396
|
+
* - Removes `generateTypeDeclarations` (types are now auto-generated via the `types` output target)
|
|
1397
|
+
*/
|
|
1398
|
+
const outputTargetRenamesRule = {
|
|
1399
|
+
id: "output-target-renames",
|
|
1400
|
+
name: "Output Target Renames",
|
|
1401
|
+
description: "Rename output targets to new v5 names",
|
|
1402
|
+
fromVersion: "4.x",
|
|
1403
|
+
toVersion: "5.x",
|
|
1404
|
+
detect(sourceFile) {
|
|
1405
|
+
const matches = [];
|
|
1406
|
+
const oldToNewNames = {
|
|
1407
|
+
dist: "loader-bundle",
|
|
1408
|
+
"dist-custom-elements": "standalone",
|
|
1409
|
+
"dist-hydrate-script": "ssr",
|
|
1410
|
+
"dist-collection": "collection",
|
|
1411
|
+
"dist-types": "types"
|
|
1412
|
+
};
|
|
1413
|
+
const visit = (node) => {
|
|
1414
|
+
if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.name) && node.name.text === "type" && ts.isStringLiteral(node.initializer)) {
|
|
1415
|
+
const typeValue = node.initializer.text;
|
|
1416
|
+
if (typeValue in oldToNewNames) {
|
|
1417
|
+
const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
|
|
1418
|
+
matches.push({
|
|
1419
|
+
node,
|
|
1420
|
+
message: `Output target type '${typeValue}' → '${oldToNewNames[typeValue]}'`,
|
|
1421
|
+
line: line + 1,
|
|
1422
|
+
column: character + 1
|
|
1423
|
+
});
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.name) && (node.name.text === "collectionDir" || node.name.text === "typesDir")) {
|
|
1427
|
+
const parent = node.parent;
|
|
1428
|
+
if (ts.isObjectLiteralExpression(parent)) {
|
|
1429
|
+
if (parent.properties.some((p) => ts.isPropertyAssignment(p) && ts.isIdentifier(p.name) && p.name.text === "type")) {
|
|
1430
|
+
const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
|
|
1431
|
+
const propName = node.name.text;
|
|
1432
|
+
const newType = propName === "collectionDir" ? "collection" : "types";
|
|
1433
|
+
matches.push({
|
|
1434
|
+
node,
|
|
1435
|
+
message: `Property '${propName}' will be extracted to separate '${newType}' output target`,
|
|
1436
|
+
line: line + 1,
|
|
1437
|
+
column: character + 1
|
|
1438
|
+
});
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.name) && node.name.text === "esmLoaderPath") {
|
|
1443
|
+
const parent = node.parent;
|
|
1444
|
+
if (ts.isObjectLiteralExpression(parent)) {
|
|
1445
|
+
if (parent.properties.some((p) => ts.isPropertyAssignment(p) && ts.isIdentifier(p.name) && p.name.text === "type")) {
|
|
1446
|
+
const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
|
|
1447
|
+
matches.push({
|
|
1448
|
+
node,
|
|
1449
|
+
message: `Property 'esmLoaderPath' renamed to 'loaderPath' (applies to all module formats)`,
|
|
1450
|
+
line: line + 1,
|
|
1451
|
+
column: character + 1
|
|
1452
|
+
});
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.name) && (node.name.text === "isPrimaryPackageOutputTarget" || node.name.text === "generateTypeDeclarations")) {
|
|
1457
|
+
const parent = node.parent;
|
|
1458
|
+
if (ts.isObjectLiteralExpression(parent)) {
|
|
1459
|
+
if (parent.properties.some((p) => ts.isPropertyAssignment(p) && ts.isIdentifier(p.name) && p.name.text === "type")) {
|
|
1460
|
+
const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
|
|
1461
|
+
const propName = node.name.text;
|
|
1462
|
+
const reason = propName === "isPrimaryPackageOutputTarget" ? "Package.json validation now auto-detects based on configured outputs" : "Types are now auto-generated via the 'types' output target";
|
|
1463
|
+
matches.push({
|
|
1464
|
+
node,
|
|
1465
|
+
message: `Property '${propName}' is removed in v5. ${reason}`,
|
|
1466
|
+
line: line + 1,
|
|
1467
|
+
column: character + 1
|
|
1468
|
+
});
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
}
|
|
1472
|
+
ts.forEachChild(node, visit);
|
|
1473
|
+
};
|
|
1474
|
+
visit(sourceFile);
|
|
1475
|
+
return matches;
|
|
1476
|
+
},
|
|
1477
|
+
transform(sourceFile, matches) {
|
|
1478
|
+
if (matches.length === 0) return sourceFile.getFullText();
|
|
1479
|
+
let text = sourceFile.getFullText();
|
|
1480
|
+
const oldToNewNames = {
|
|
1481
|
+
dist: "loader-bundle",
|
|
1482
|
+
"dist-custom-elements": "standalone",
|
|
1483
|
+
"dist-hydrate-script": "ssr",
|
|
1484
|
+
"dist-collection": "collection",
|
|
1485
|
+
"dist-types": "types"
|
|
1486
|
+
};
|
|
1487
|
+
const outputTargetsToExtract = [];
|
|
1488
|
+
const findOutputTargetsToExtract = (node) => {
|
|
1489
|
+
if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.name) && node.name.text === "outputTargets" && ts.isArrayLiteralExpression(node.initializer)) {
|
|
1490
|
+
for (const element of node.initializer.elements) if (ts.isObjectLiteralExpression(element)) {
|
|
1491
|
+
let collectionDir;
|
|
1492
|
+
let typesDir;
|
|
1493
|
+
for (const prop of element.properties) if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name)) {
|
|
1494
|
+
if (prop.name.text === "collectionDir" && ts.isStringLiteral(prop.initializer)) collectionDir = prop.initializer.text;
|
|
1495
|
+
else if (prop.name.text === "typesDir" && ts.isStringLiteral(prop.initializer)) typesDir = prop.initializer.text;
|
|
1496
|
+
}
|
|
1497
|
+
if (collectionDir || typesDir) outputTargetsToExtract.push({
|
|
1498
|
+
targetNode: element,
|
|
1499
|
+
collectionDir,
|
|
1500
|
+
typesDir
|
|
1501
|
+
});
|
|
1502
|
+
}
|
|
1503
|
+
}
|
|
1504
|
+
ts.forEachChild(node, findOutputTargetsToExtract);
|
|
1505
|
+
};
|
|
1506
|
+
findOutputTargetsToExtract(sourceFile);
|
|
1507
|
+
const modifications = [];
|
|
1508
|
+
for (const match of matches) {
|
|
1509
|
+
const prop = match.node;
|
|
1510
|
+
const propName = prop.name.text;
|
|
1511
|
+
if (propName === "type" && ts.isStringLiteral(prop.initializer)) {
|
|
1512
|
+
const newType = oldToNewNames[prop.initializer.text];
|
|
1513
|
+
if (newType) {
|
|
1514
|
+
const start = prop.initializer.getStart() + 1;
|
|
1515
|
+
const end = prop.initializer.getEnd() - 1;
|
|
1516
|
+
modifications.push({
|
|
1517
|
+
type: "replace",
|
|
1518
|
+
start,
|
|
1519
|
+
end,
|
|
1520
|
+
replacement: newType
|
|
1521
|
+
});
|
|
1522
|
+
}
|
|
1523
|
+
} else if (propName === "esmLoaderPath") {
|
|
1524
|
+
const nameNode = prop.name;
|
|
1525
|
+
modifications.push({
|
|
1526
|
+
type: "replace",
|
|
1527
|
+
start: nameNode.getStart(),
|
|
1528
|
+
end: nameNode.getEnd(),
|
|
1529
|
+
replacement: "loaderPath"
|
|
1530
|
+
});
|
|
1531
|
+
if (ts.isStringLiteral(prop.initializer)) {
|
|
1532
|
+
const newPath = "../" + prop.initializer.text;
|
|
1533
|
+
modifications.push({
|
|
1534
|
+
type: "replace",
|
|
1535
|
+
start: prop.initializer.getStart() + 1,
|
|
1536
|
+
end: prop.initializer.getEnd() - 1,
|
|
1537
|
+
replacement: newPath
|
|
1538
|
+
});
|
|
1539
|
+
}
|
|
1540
|
+
} else if (propName === "collectionDir" || propName === "typesDir" || propName === "isPrimaryPackageOutputTarget" || propName === "generateTypeDeclarations") {
|
|
1541
|
+
const start = prop.getStart();
|
|
1542
|
+
let end = prop.getEnd();
|
|
1543
|
+
const beforeProp = text.slice(0, start).match(/,\s*$/);
|
|
1544
|
+
const afterProp = text.slice(end).match(/^\s*,/);
|
|
1545
|
+
if (afterProp) end = end + afterProp[0].length;
|
|
1546
|
+
else if (beforeProp) {
|
|
1547
|
+
const commaStart = start - beforeProp[0].length;
|
|
1548
|
+
modifications.push({
|
|
1549
|
+
type: "remove",
|
|
1550
|
+
start: commaStart,
|
|
1551
|
+
end
|
|
1552
|
+
});
|
|
1553
|
+
continue;
|
|
1554
|
+
}
|
|
1555
|
+
modifications.push({
|
|
1556
|
+
type: "remove",
|
|
1557
|
+
start,
|
|
1558
|
+
end
|
|
1559
|
+
});
|
|
1560
|
+
}
|
|
1561
|
+
}
|
|
1562
|
+
modifications.sort((a, b) => b.start - a.start);
|
|
1563
|
+
for (const mod of modifications) if (mod.type === "replace") text = text.slice(0, mod.start) + mod.replacement + text.slice(mod.end);
|
|
1564
|
+
else if (mod.type === "remove") text = text.slice(0, mod.start) + text.slice(mod.end);
|
|
1565
|
+
if (outputTargetsToExtract.length > 0) text = addExtractedOutputTargets(text, sourceFile, outputTargetsToExtract);
|
|
1566
|
+
text = text.replace(/,\s*\n\s*\n/g, ",\n");
|
|
1567
|
+
text = text.replace(/{\s*\n\s*\n/g, "{\n");
|
|
1568
|
+
text = text.replace(/,\s*,/g, ",");
|
|
1569
|
+
return text;
|
|
1570
|
+
}
|
|
1571
|
+
};
|
|
1572
|
+
/**
|
|
1573
|
+
* Add new output targets for extracted collectionDir and typesDir.
|
|
1574
|
+
* @param text The original source text
|
|
1575
|
+
* @param sourceFile The TypeScript source file object
|
|
1576
|
+
* @param toExtract An array of objects containing the output target nodes and their collectionDir/typesDir values to extract
|
|
1577
|
+
* @returns The modified source text with new output targets added
|
|
1578
|
+
*/
|
|
1579
|
+
function addExtractedOutputTargets(text, sourceFile, toExtract) {
|
|
1580
|
+
let outputTargetsArray = null;
|
|
1581
|
+
const findOutputTargets = (node) => {
|
|
1582
|
+
if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.name) && node.name.text === "outputTargets" && ts.isArrayLiteralExpression(node.initializer)) outputTargetsArray = node.initializer;
|
|
1583
|
+
ts.forEachChild(node, findOutputTargets);
|
|
1584
|
+
};
|
|
1585
|
+
findOutputTargets(sourceFile);
|
|
1586
|
+
if (!outputTargetsArray || toExtract.length === 0) return text;
|
|
1587
|
+
const array = outputTargetsArray;
|
|
1588
|
+
let indent = " ";
|
|
1589
|
+
if (array.elements.length > 0) {
|
|
1590
|
+
const elemStart = array.elements[0].getStart();
|
|
1591
|
+
const lineStart = text.lastIndexOf("\n", elemStart) + 1;
|
|
1592
|
+
const leadingWhitespace = text.slice(lineStart, elemStart);
|
|
1593
|
+
if (/^\s+$/.test(leadingWhitespace)) indent = leadingWhitespace;
|
|
1594
|
+
}
|
|
1595
|
+
const lastElement = array.elements[array.elements.length - 1];
|
|
1596
|
+
if (!lastElement) return text;
|
|
1597
|
+
let insertPos = lastElement.getEnd();
|
|
1598
|
+
const afterLastElement = text.slice(insertPos).match(/^(\s*,)?/);
|
|
1599
|
+
if (afterLastElement && afterLastElement[1]) insertPos += afterLastElement[0].length;
|
|
1600
|
+
const newTargets = [];
|
|
1601
|
+
for (const extracted of toExtract) {
|
|
1602
|
+
if (extracted.collectionDir) newTargets.push(`{\n${indent} type: 'collection',\n${indent} dir: '${extracted.collectionDir}',\n${indent}}`);
|
|
1603
|
+
if (extracted.typesDir) newTargets.push(`{\n${indent} type: 'types',\n${indent} dir: '${extracted.typesDir}',\n${indent}}`);
|
|
1604
|
+
}
|
|
1605
|
+
if (newTargets.length === 0) return text;
|
|
1606
|
+
const insertion = ",\n" + newTargets.map((t) => `${indent}${t}`).join(",\n");
|
|
1607
|
+
text = text.slice(0, insertPos) + insertion + text.slice(insertPos);
|
|
1608
|
+
return text;
|
|
1609
|
+
}
|
|
1610
|
+
//#endregion
|
|
1611
|
+
//#region src/migrations/rules/rolldown-config.ts
|
|
1612
|
+
/**
|
|
1613
|
+
* NodeResolve fields that were valid in @rollup/plugin-node-resolve but don't exist
|
|
1614
|
+
* in rolldown's native resolver and should be removed.
|
|
1615
|
+
*/
|
|
1616
|
+
const REMOVED_NODE_RESOLVE_FIELDS = new Set([
|
|
1617
|
+
"browser",
|
|
1618
|
+
"modulePaths",
|
|
1619
|
+
"dedupe",
|
|
1620
|
+
"jail",
|
|
1621
|
+
"modulesOnly",
|
|
1622
|
+
"preferBuiltins",
|
|
1623
|
+
"resolveOnly",
|
|
1624
|
+
"rootDir",
|
|
1625
|
+
"allowExportsFolderMapping"
|
|
1626
|
+
]);
|
|
1627
|
+
/** Fields renamed between @rollup/plugin-node-resolve and rolldown's native resolver. */
|
|
1628
|
+
const RENAMED_NODE_RESOLVE_FIELDS = {
|
|
1629
|
+
exportConditions: "conditionNames",
|
|
1630
|
+
moduleDirectories: "modules"
|
|
1631
|
+
};
|
|
1632
|
+
function isInsideNodeResolve(node) {
|
|
1633
|
+
const parent = node.parent;
|
|
1634
|
+
if (!ts.isObjectLiteralExpression(parent)) return false;
|
|
1635
|
+
const grandParent = parent.parent;
|
|
1636
|
+
return ts.isPropertyAssignment(grandParent) && ts.isIdentifier(grandParent.name) && grandParent.name.text === "nodeResolve";
|
|
1637
|
+
}
|
|
1638
|
+
/**
|
|
1639
|
+
* Migration rule for rolldown-related config changes in Stencil v5.
|
|
1640
|
+
*
|
|
1641
|
+
* Handles:
|
|
1642
|
+
* - `rollupConfig` → `rolldownConfig` (key rename + flatten `inputOptions`)
|
|
1643
|
+
* - `rolldownConfig: { inputOptions: { ... } }` → `rolldownConfig: { ... }` (flatten)
|
|
1644
|
+
* - `rollupPlugins` → `rolldownPlugins`
|
|
1645
|
+
* - `nodeResolve.exportConditions` → `nodeResolve.conditionNames`
|
|
1646
|
+
* - `nodeResolve.moduleDirectories` → `nodeResolve.modules`
|
|
1647
|
+
* - Removes unsupported `nodeResolve` fields (`browser`, `dedupe`, `jail`, etc.)
|
|
1648
|
+
*/
|
|
1649
|
+
const rolldownConfigRule = {
|
|
1650
|
+
id: "rolldown-config",
|
|
1651
|
+
name: "Rolldown Config Migration",
|
|
1652
|
+
description: "Migrate rollup/rolldown config options to v5 rolldown-native API",
|
|
1653
|
+
fromVersion: "4.x",
|
|
1654
|
+
toVersion: "5.x",
|
|
1655
|
+
detect(sourceFile) {
|
|
1656
|
+
const matches = [];
|
|
1657
|
+
const visit = (node) => {
|
|
1658
|
+
if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.name)) {
|
|
1659
|
+
const name = node.name.text;
|
|
1660
|
+
const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
|
|
1661
|
+
if (name === "rollupPlugins") matches.push({
|
|
1662
|
+
node,
|
|
1663
|
+
message: `'rollupPlugins' renamed to 'rolldownPlugins'`,
|
|
1664
|
+
line: line + 1,
|
|
1665
|
+
column: character + 1
|
|
1666
|
+
});
|
|
1667
|
+
else if (name === "rollupConfig" || name === "rolldownConfig") {
|
|
1668
|
+
const value = node.initializer;
|
|
1669
|
+
if (name === "rollupConfig" || ts.isObjectLiteralExpression(value) && value.properties.some((p) => ts.isPropertyAssignment(p) && ts.isIdentifier(p.name) && p.name.text === "inputOptions")) matches.push({
|
|
1670
|
+
node,
|
|
1671
|
+
message: name === "rollupConfig" ? `'rollupConfig' renamed to 'rolldownConfig'; 'inputOptions' wrapper removed` : `'rolldownConfig.inputOptions' wrapper removed - options are now top-level`,
|
|
1672
|
+
line: line + 1,
|
|
1673
|
+
column: character + 1
|
|
1674
|
+
});
|
|
1675
|
+
} else if (name in RENAMED_NODE_RESOLVE_FIELDS && isInsideNodeResolve(node)) matches.push({
|
|
1676
|
+
node,
|
|
1677
|
+
message: `'nodeResolve.${name}' renamed to 'nodeResolve.${RENAMED_NODE_RESOLVE_FIELDS[name]}'`,
|
|
1678
|
+
line: line + 1,
|
|
1679
|
+
column: character + 1
|
|
1680
|
+
});
|
|
1681
|
+
else if (REMOVED_NODE_RESOLVE_FIELDS.has(name) && isInsideNodeResolve(node)) matches.push({
|
|
1682
|
+
node,
|
|
1683
|
+
message: `'nodeResolve.${name}' is not supported by rolldown's native resolver and will be removed`,
|
|
1684
|
+
line: line + 1,
|
|
1685
|
+
column: character + 1
|
|
1686
|
+
});
|
|
1687
|
+
}
|
|
1688
|
+
ts.forEachChild(node, visit);
|
|
1689
|
+
};
|
|
1690
|
+
visit(sourceFile);
|
|
1691
|
+
return matches;
|
|
1692
|
+
},
|
|
1693
|
+
transform(sourceFile, matches) {
|
|
1694
|
+
if (matches.length === 0) return sourceFile.getFullText();
|
|
1695
|
+
const edits = [];
|
|
1696
|
+
const fullText = sourceFile.getFullText();
|
|
1697
|
+
for (const match of matches) {
|
|
1698
|
+
const node = match.node;
|
|
1699
|
+
const name = node.name.text;
|
|
1700
|
+
if (name === "rollupPlugins") {
|
|
1701
|
+
const nameStart = node.name.getStart(sourceFile);
|
|
1702
|
+
const nameEnd = node.name.getEnd();
|
|
1703
|
+
edits.push({
|
|
1704
|
+
start: nameStart,
|
|
1705
|
+
end: nameEnd,
|
|
1706
|
+
replacement: "rolldownPlugins"
|
|
1707
|
+
});
|
|
1708
|
+
continue;
|
|
1709
|
+
}
|
|
1710
|
+
if (name in RENAMED_NODE_RESOLVE_FIELDS && isInsideNodeResolve(node)) {
|
|
1711
|
+
const nameStart = node.name.getStart(sourceFile);
|
|
1712
|
+
const nameEnd = node.name.getEnd();
|
|
1713
|
+
edits.push({
|
|
1714
|
+
start: nameStart,
|
|
1715
|
+
end: nameEnd,
|
|
1716
|
+
replacement: RENAMED_NODE_RESOLVE_FIELDS[name]
|
|
1717
|
+
});
|
|
1718
|
+
continue;
|
|
1719
|
+
}
|
|
1720
|
+
if (REMOVED_NODE_RESOLVE_FIELDS.has(name) && isInsideNodeResolve(node)) {
|
|
1721
|
+
const start = node.getFullStart();
|
|
1722
|
+
const end = node.getEnd();
|
|
1723
|
+
const trailingComma = fullText.slice(end).match(/^\s*,/);
|
|
1724
|
+
edits.push({
|
|
1725
|
+
start,
|
|
1726
|
+
end: trailingComma ? end + trailingComma[0].length : end,
|
|
1727
|
+
replacement: ""
|
|
1728
|
+
});
|
|
1729
|
+
continue;
|
|
1730
|
+
}
|
|
1731
|
+
if (name === "rollupConfig" || name === "rolldownConfig") {
|
|
1732
|
+
const value = node.initializer;
|
|
1733
|
+
if (!ts.isObjectLiteralExpression(value)) {
|
|
1734
|
+
if (name === "rollupConfig") {
|
|
1735
|
+
const nameStart = node.name.getStart(sourceFile);
|
|
1736
|
+
edits.push({
|
|
1737
|
+
start: nameStart,
|
|
1738
|
+
end: node.name.getEnd(),
|
|
1739
|
+
replacement: "rolldownConfig"
|
|
1740
|
+
});
|
|
1741
|
+
}
|
|
1742
|
+
continue;
|
|
1743
|
+
}
|
|
1744
|
+
const inputOptionsProp = value.properties.find((p) => ts.isPropertyAssignment(p) && ts.isIdentifier(p.name) && p.name.text === "inputOptions");
|
|
1745
|
+
if (!inputOptionsProp) {
|
|
1746
|
+
if (name === "rollupConfig") {
|
|
1747
|
+
const nameStart = node.name.getStart(sourceFile);
|
|
1748
|
+
edits.push({
|
|
1749
|
+
start: nameStart,
|
|
1750
|
+
end: node.name.getEnd(),
|
|
1751
|
+
replacement: "rolldownConfig"
|
|
1752
|
+
});
|
|
1753
|
+
}
|
|
1754
|
+
continue;
|
|
1755
|
+
}
|
|
1756
|
+
const newValueText = inputOptionsProp.initializer.getText(sourceFile);
|
|
1757
|
+
const keyStart = node.name.getStart(sourceFile);
|
|
1758
|
+
edits.push({
|
|
1759
|
+
start: keyStart,
|
|
1760
|
+
end: node.getEnd(),
|
|
1761
|
+
replacement: `rolldownConfig: ${newValueText}`
|
|
1762
|
+
});
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1765
|
+
edits.sort((a, b) => b.start - a.start);
|
|
1766
|
+
let text = fullText;
|
|
1767
|
+
for (const edit of edits) text = text.slice(0, edit.start) + edit.replacement + text.slice(edit.end);
|
|
1768
|
+
return text;
|
|
1769
|
+
}
|
|
1770
|
+
};
|
|
1771
|
+
//#endregion
|
|
823
1772
|
//#region src/migrations/index.ts
|
|
824
1773
|
/**
|
|
825
1774
|
* Build a map of local import names to their original names from @stencil/core.
|
|
@@ -857,7 +1806,18 @@ const isStencilDecorator = (decoratorName, expectedOriginalName, importMap) => {
|
|
|
857
1806
|
* Registry of all available migration rules.
|
|
858
1807
|
* Rules are applied in order, so add new rules at the end.
|
|
859
1808
|
*/
|
|
860
|
-
const migrationRules = [
|
|
1809
|
+
const migrationRules = [
|
|
1810
|
+
encapsulationApiRule,
|
|
1811
|
+
formAssociatedRule,
|
|
1812
|
+
buildDistDocsRule,
|
|
1813
|
+
outputTargetRenamesRule,
|
|
1814
|
+
devModeRule,
|
|
1815
|
+
globalStyleInjectRule,
|
|
1816
|
+
lightDomPatchesRule,
|
|
1817
|
+
externalRuntimeRule,
|
|
1818
|
+
hashFileNamesRule,
|
|
1819
|
+
rolldownConfigRule
|
|
1820
|
+
];
|
|
861
1821
|
/**
|
|
862
1822
|
* Get all migration rules for a specific version upgrade.
|
|
863
1823
|
* @param fromVersion Source version (e.g., '4')
|
|
@@ -1041,27 +2001,49 @@ async function getTypeScriptFiles(config, sys, logger) {
|
|
|
1041
2001
|
return [];
|
|
1042
2002
|
}
|
|
1043
2003
|
if (results.errors && results.errors.length > 0) for (const err of results.errors) logger.warn(ts.flattenDiagnosticMessageText(err.messageText, "\n"));
|
|
1044
|
-
|
|
2004
|
+
const files = results.fileNames.filter((f) => (f.endsWith(".ts") || f.endsWith(".tsx")) && !f.endsWith(".d.ts"));
|
|
2005
|
+
const configFile = config.configPath;
|
|
2006
|
+
if (configFile && (configFile.endsWith(".ts") || configFile.endsWith(".mts"))) {
|
|
2007
|
+
if (!files.includes(configFile)) files.push(configFile);
|
|
2008
|
+
const configContent = await sys.readFile(configFile);
|
|
2009
|
+
if (configContent) {
|
|
2010
|
+
const configSourceFile = ts.createSourceFile(configFile, configContent, ts.ScriptTarget.Latest, true);
|
|
2011
|
+
const configDir = dirname(configFile);
|
|
2012
|
+
for (const statement of configSourceFile.statements) if (ts.isImportDeclaration(statement) && ts.isStringLiteral(statement.moduleSpecifier)) {
|
|
2013
|
+
const specifier = statement.moduleSpecifier.text;
|
|
2014
|
+
if (!specifier.startsWith("./") && !specifier.startsWith("../")) continue;
|
|
2015
|
+
const basePath = join(configDir, specifier);
|
|
2016
|
+
const candidates = basePath.endsWith(".ts") || basePath.endsWith(".tsx") ? [basePath] : [`${basePath}.ts`, `${basePath}.tsx`];
|
|
2017
|
+
for (const candidate of candidates) if (!candidate.endsWith(".d.ts") && !files.includes(candidate) && candidate.startsWith(config.rootDir)) {
|
|
2018
|
+
if (await sys.readFile(candidate)) {
|
|
2019
|
+
files.push(candidate);
|
|
2020
|
+
break;
|
|
2021
|
+
}
|
|
2022
|
+
}
|
|
2023
|
+
}
|
|
2024
|
+
}
|
|
2025
|
+
}
|
|
2026
|
+
return files;
|
|
1045
2027
|
}
|
|
1046
2028
|
//#endregion
|
|
1047
2029
|
//#region src/task-prerender.ts
|
|
1048
2030
|
const taskPrerender = async (coreCompiler, config, flags) => {
|
|
1049
2031
|
startupCompilerLog(coreCompiler, config);
|
|
1050
|
-
const
|
|
1051
|
-
if (typeof
|
|
2032
|
+
const ssrAppFilePath = flags.unknownArgs[0];
|
|
2033
|
+
if (typeof ssrAppFilePath !== "string") {
|
|
1052
2034
|
config.logger.error(`Missing hydrate app script path`);
|
|
1053
2035
|
return config.sys.exit(1);
|
|
1054
2036
|
}
|
|
1055
2037
|
const srcIndexHtmlPath = config.srcIndexHtml;
|
|
1056
|
-
const diagnostics = await runPrerenderTask(coreCompiler, config,
|
|
2038
|
+
const diagnostics = await runPrerenderTask(coreCompiler, config, ssrAppFilePath, void 0, srcIndexHtmlPath);
|
|
1057
2039
|
config.logger.printDiagnostics(diagnostics);
|
|
1058
2040
|
if (diagnostics.some((d) => d.level === "error")) return config.sys.exit(1);
|
|
1059
2041
|
};
|
|
1060
|
-
const runPrerenderTask = async (coreCompiler, config,
|
|
2042
|
+
const runPrerenderTask = async (coreCompiler, config, ssrAppFilePath, componentGraph, srcIndexHtmlPath) => {
|
|
1061
2043
|
const diagnostics = [];
|
|
1062
2044
|
try {
|
|
1063
2045
|
const results = await (await coreCompiler.createPrerenderer(config)).start({
|
|
1064
|
-
|
|
2046
|
+
ssrAppFilePath,
|
|
1065
2047
|
componentGraph,
|
|
1066
2048
|
srcIndexHtmlPath
|
|
1067
2049
|
});
|
|
@@ -1393,7 +2375,7 @@ const anonymizeConfigForTelemetry = (config) => {
|
|
|
1393
2375
|
if (OUTPUT_TARGET_KEYS_TO_KEEP.includes(key)) return value;
|
|
1394
2376
|
return "omitted";
|
|
1395
2377
|
}));
|
|
1396
|
-
if (
|
|
2378
|
+
if (isOutputTargetSsr(target) && target.external) anonymizedOT["external"] = target.external.concat();
|
|
1397
2379
|
return anonymizedOT;
|
|
1398
2380
|
});
|
|
1399
2381
|
for (const prop of CONFIG_PROPS_TO_DELETE) delete anonymizedConfig[prop];
|
|
@@ -1587,6 +2569,23 @@ const taskBuild = async (coreCompiler, config, flags) => {
|
|
|
1587
2569
|
let exitCode = 0;
|
|
1588
2570
|
try {
|
|
1589
2571
|
startupCompilerLog(coreCompiler, config);
|
|
2572
|
+
const preBuildMigrationResult = await detectMigrations(coreCompiler, config);
|
|
2573
|
+
if (preBuildMigrationResult.hasMigrations) {
|
|
2574
|
+
const action = await promptForMigration(config, preBuildMigrationResult, "pre-build");
|
|
2575
|
+
if (action === "run") {
|
|
2576
|
+
await taskMigrate(coreCompiler, config, {
|
|
2577
|
+
...flags,
|
|
2578
|
+
dryRun: false
|
|
2579
|
+
});
|
|
2580
|
+
config.logger.info("\nMigrations applied. Starting build...\n");
|
|
2581
|
+
} else if (action === "dry-run") {
|
|
2582
|
+
await taskMigrate(coreCompiler, config, {
|
|
2583
|
+
...flags,
|
|
2584
|
+
dryRun: true
|
|
2585
|
+
});
|
|
2586
|
+
return config.sys.exit(1);
|
|
2587
|
+
} else return config.sys.exit(1);
|
|
2588
|
+
}
|
|
1590
2589
|
const versionChecker = startCheckVersion(config, coreCompiler.version, flags);
|
|
1591
2590
|
const compiler = await coreCompiler.createCompiler(config);
|
|
1592
2591
|
const results = await compiler.build();
|
|
@@ -1595,7 +2594,7 @@ const taskBuild = async (coreCompiler, config, flags) => {
|
|
|
1595
2594
|
if (results.hasError) {
|
|
1596
2595
|
const migrationResult = await detectMigrations(coreCompiler, config);
|
|
1597
2596
|
if (migrationResult.hasMigrations) {
|
|
1598
|
-
const action = await
|
|
2597
|
+
const action = await promptForMigration(config, migrationResult, "post-error");
|
|
1599
2598
|
if (action === "run") {
|
|
1600
2599
|
await taskMigrate(coreCompiler, config, {
|
|
1601
2600
|
...flags,
|
|
@@ -1608,7 +2607,7 @@ const taskBuild = async (coreCompiler, config, flags) => {
|
|
|
1608
2607
|
if (!newResults.hasError) {
|
|
1609
2608
|
exitCode = 0;
|
|
1610
2609
|
if (flags.prerender) {
|
|
1611
|
-
const prerenderDiagnostics = await runPrerenderTask(coreCompiler, config, newResults.
|
|
2610
|
+
const prerenderDiagnostics = await runPrerenderTask(coreCompiler, config, newResults.ssrAppFilePath, newResults.componentGraph, void 0);
|
|
1612
2611
|
config.logger.printDiagnostics(prerenderDiagnostics);
|
|
1613
2612
|
if (prerenderDiagnostics.some((d) => d.level === "error")) exitCode = 1;
|
|
1614
2613
|
}
|
|
@@ -1622,7 +2621,7 @@ const taskBuild = async (coreCompiler, config, flags) => {
|
|
|
1622
2621
|
} else exitCode = 1;
|
|
1623
2622
|
} else exitCode = 1;
|
|
1624
2623
|
} else if (flags.prerender) {
|
|
1625
|
-
const prerenderDiagnostics = await runPrerenderTask(coreCompiler, config, results.
|
|
2624
|
+
const prerenderDiagnostics = await runPrerenderTask(coreCompiler, config, results.ssrAppFilePath, results.componentGraph, void 0);
|
|
1626
2625
|
config.logger.printDiagnostics(prerenderDiagnostics);
|
|
1627
2626
|
if (prerenderDiagnostics.some((d) => d.level === "error")) exitCode = 1;
|
|
1628
2627
|
}
|
|
@@ -1634,24 +2633,26 @@ const taskBuild = async (coreCompiler, config, flags) => {
|
|
|
1634
2633
|
if (exitCode > 0) return config.sys.exit(exitCode);
|
|
1635
2634
|
};
|
|
1636
2635
|
/**
|
|
1637
|
-
* Prompt the user about available migrations
|
|
2636
|
+
* Prompt the user about available migrations.
|
|
1638
2637
|
* Shows what migrations are available and lets them choose to run them.
|
|
1639
2638
|
* @param config the Stencil config
|
|
1640
2639
|
* @param migrationResult the result of migration detection with available migrations
|
|
2640
|
+
* @param context whether this is a pre-build check or post-error check
|
|
1641
2641
|
* @returns the user's chosen action for handling migrations
|
|
1642
2642
|
*/
|
|
1643
|
-
async function
|
|
2643
|
+
async function promptForMigration(config, migrationResult, context) {
|
|
1644
2644
|
const logger = config.logger;
|
|
1645
2645
|
logger.info("");
|
|
1646
|
-
logger.info(logger.bold(logger.yellow("Migrations
|
|
2646
|
+
logger.info(logger.bold(logger.yellow("Migrations Required")));
|
|
1647
2647
|
logger.info("─".repeat(40));
|
|
1648
|
-
logger.info(`Found ${migrationResult.totalMatches} item(s) in ${migrationResult.filesAffected} file(s) that
|
|
2648
|
+
logger.info(`Found ${migrationResult.totalMatches} item(s) in ${migrationResult.filesAffected} file(s) that need to be migrated for Stencil v5.`);
|
|
1649
2649
|
for (const migration of migrationResult.migrations) {
|
|
1650
2650
|
const relPath = relative(config.rootDir, migration.filePath);
|
|
1651
2651
|
logger.info(` ${logger.cyan(relPath)}: ${migration.matches.length} item(s)`);
|
|
1652
2652
|
}
|
|
1653
2653
|
logger.info("");
|
|
1654
|
-
logger.info("
|
|
2654
|
+
if (context === "pre-build") logger.info("Your config contains deprecated options that must be migrated before building.");
|
|
2655
|
+
else logger.info("These migrations may help resolve the build errors above.");
|
|
1655
2656
|
const prompt = (await import("prompts")).default;
|
|
1656
2657
|
const response = await prompt({
|
|
1657
2658
|
name: "action",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stencil/cli",
|
|
3
|
-
"version": "5.0.0-alpha.
|
|
3
|
+
"version": "5.0.0-alpha.6",
|
|
4
4
|
"description": "CLI for Stencil - Web component compiler",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"components",
|
|
@@ -37,15 +37,15 @@
|
|
|
37
37
|
"./cli": "./bin/stencil.mjs"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"prompts": "^2.
|
|
41
|
-
"@stencil/dev-server": "5.0.0-alpha.
|
|
40
|
+
"prompts": "^2.0.0",
|
|
41
|
+
"@stencil/dev-server": "5.0.0-alpha.6"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@types/prompts": "^2.4.9",
|
|
45
|
-
"tsdown": "
|
|
46
|
-
"typescript": "
|
|
47
|
-
"vitest": "^4.1.
|
|
48
|
-
"@stencil/core": "5.0.0-alpha.
|
|
45
|
+
"tsdown": ">=0.21.0 <1.0.0",
|
|
46
|
+
"typescript": ">4.0.0 <7.0.0",
|
|
47
|
+
"vitest": "^4.1.7",
|
|
48
|
+
"@stencil/core": "5.0.0-alpha.6"
|
|
49
49
|
},
|
|
50
50
|
"peerDependencies": {
|
|
51
51
|
"@stencil/core": "^5.0.0-0"
|