@fluid-app/fluid-cli-portal 0.1.32 → 0.1.34
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.map +1 -1
- package/dist/index.mjs +1261 -35
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
- package/templates/base/.gitignore.template +27 -0
- package/templates/base/AGENTS.md +36 -0
- package/templates/base/skills/fluid-portal-authoring/SKILL.md +153 -0
- package/templates/base/src/main.tsx +2 -2
- package/templates/base/src/portal.config.ts +8 -34
- package/templates/starter/README.md.template +122 -181
- package/templates/base/src/navigation.config.ts +0 -26
- package/templates/base/src/screens/Dashboard.tsx +0 -133
- package/templates/base/src/screens/ExampleForm.tsx +0 -177
package/dist/index.mjs
CHANGED
|
@@ -169,10 +169,39 @@ function processTemplate(content, variables, isTemplate, filePath) {
|
|
|
169
169
|
throw new Error(`Template processing failed${filePath ? ` for ${filePath}` : ""}: ${message}`);
|
|
170
170
|
}
|
|
171
171
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
172
|
+
async function getSharedTemplateSkillFiles() {
|
|
173
|
+
const skillsRoot = join(findFluidCliPackageRoot(), "template-skills");
|
|
174
|
+
const files = await getFiles(skillsRoot);
|
|
175
|
+
return Promise.all(files.map(async (file) => ({
|
|
176
|
+
relativePath: join("skills", file),
|
|
177
|
+
content: await readFile(join(skillsRoot, file), "utf-8")
|
|
178
|
+
})));
|
|
179
|
+
}
|
|
180
|
+
function findFluidCliPackageRoot() {
|
|
181
|
+
const workspacePackageRoot = join(findPackageRoot(), "..", "core");
|
|
182
|
+
if (existsSync(join(workspacePackageRoot, "template-skills"))) return workspacePackageRoot;
|
|
183
|
+
let dir = dirname(fileURLToPath(import.meta.resolve("@fluid-app/fluid-cli")));
|
|
184
|
+
while (!existsSync(join(dir, "package.json"))) {
|
|
185
|
+
const parent = dirname(dir);
|
|
186
|
+
if (parent === dir) throw new Error("Could not find Fluid CLI package root");
|
|
187
|
+
dir = parent;
|
|
188
|
+
}
|
|
189
|
+
return dir;
|
|
190
|
+
}
|
|
191
|
+
async function writeOutputFiles(targetPath, outputFiles, content) {
|
|
192
|
+
for (const outputFile of outputFiles) {
|
|
193
|
+
const destPath = join(targetPath, outputFile);
|
|
194
|
+
await mkdir(dirname(destPath), { recursive: true });
|
|
195
|
+
await writeFile(destPath, content, "utf-8");
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
function getOutputFilenames(filename) {
|
|
199
|
+
const outputFilename = getOutputFilename(filename);
|
|
200
|
+
const normalized = outputFilename.replace(/\\/g, "/");
|
|
201
|
+
if (normalized === "AGENTS.md") return ["AGENTS.md", "CLAUDE.md"];
|
|
202
|
+
if (normalized.startsWith("skills/")) return [join(".agents", outputFilename), join(".claude", outputFilename)];
|
|
203
|
+
return [outputFilename];
|
|
204
|
+
}
|
|
176
205
|
function getOutputFilename(filename) {
|
|
177
206
|
if (filename.endsWith(".template")) return filename.slice(0, -9);
|
|
178
207
|
return filename;
|
|
@@ -186,10 +215,15 @@ async function copyTemplate(templatePath, targetPath, variables) {
|
|
|
186
215
|
for (const file of files) {
|
|
187
216
|
const sourcePath = join(templatePath, file);
|
|
188
217
|
const isTemplate = file.endsWith(".template");
|
|
189
|
-
const
|
|
190
|
-
await
|
|
191
|
-
|
|
218
|
+
const outputFiles = getOutputFilenames(file);
|
|
219
|
+
const processed = processTemplate(await readFile(sourcePath, "utf-8"), variables, isTemplate, sourcePath);
|
|
220
|
+
for (const outputFile of outputFiles) {
|
|
221
|
+
const destPath = join(targetPath, outputFile);
|
|
222
|
+
await mkdir(dirname(destPath), { recursive: true });
|
|
223
|
+
await writeFile(destPath, processed, "utf-8");
|
|
224
|
+
}
|
|
192
225
|
}
|
|
226
|
+
for (const skillFile of await getSharedTemplateSkillFiles()) await writeOutputFiles(targetPath, getOutputFilenames(skillFile.relativePath), skillFile.content);
|
|
193
227
|
}
|
|
194
228
|
/**
|
|
195
229
|
* Checks if a directory exists
|
|
@@ -309,10 +343,15 @@ async function copyTemplateSafe(templatePath, targetPath, variables) {
|
|
|
309
343
|
for (const file of files) {
|
|
310
344
|
const sourcePath = join(templatePath, file);
|
|
311
345
|
const isTemplateFile = file.endsWith(".template");
|
|
312
|
-
const
|
|
313
|
-
await
|
|
314
|
-
|
|
346
|
+
const outputFiles = getOutputFilenames(file);
|
|
347
|
+
const processed = processTemplate(await readFile(sourcePath, "utf-8"), variables, isTemplateFile, sourcePath);
|
|
348
|
+
for (const outputFile of outputFiles) {
|
|
349
|
+
const destPath = join(targetPath, outputFile);
|
|
350
|
+
await mkdir(dirname(destPath), { recursive: true });
|
|
351
|
+
await writeFile(destPath, processed, "utf-8");
|
|
352
|
+
}
|
|
315
353
|
}
|
|
354
|
+
for (const skillFile of await getSharedTemplateSkillFiles()) await writeOutputFiles(targetPath, getOutputFilenames(skillFile.relativePath), skillFile.content);
|
|
316
355
|
return success(void 0);
|
|
317
356
|
} catch (err) {
|
|
318
357
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
@@ -469,7 +508,7 @@ const createCommand = new Command("create").description("Create a new Fluid port
|
|
|
469
508
|
console.log(chalk.yellow(" Run " + chalk.cyan("fluid login") + " and update " + chalk.cyan(".fluidrc") + " with your profile name."));
|
|
470
509
|
console.log();
|
|
471
510
|
}
|
|
472
|
-
console.log("
|
|
511
|
+
console.log("Run " + chalk.cyan("pnpm pull") + " and edit " + chalk.cyan("portal/") + " JSON to customize your portal definition.");
|
|
473
512
|
console.log();
|
|
474
513
|
} catch (error) {
|
|
475
514
|
console.log();
|
|
@@ -2488,18 +2527,10 @@ function validateCssUrl(value, cssUrlPath, errors) {
|
|
|
2488
2527
|
});
|
|
2489
2528
|
return;
|
|
2490
2529
|
}
|
|
2491
|
-
if (!
|
|
2492
|
-
errors.push({
|
|
2493
|
-
code: "INVALID_SOURCE_PACKAGE",
|
|
2494
|
-
path: cssUrlPath,
|
|
2495
|
-
message: "Widget package cssUrls entries must be https URLs, trusted local http URLs, or relative URLs."
|
|
2496
|
-
});
|
|
2497
|
-
return;
|
|
2498
|
-
}
|
|
2499
|
-
if (!isAbsoluteRuntimeUrl(value) && !isWidgetPackageCssArtifactPath(value)) errors.push({
|
|
2530
|
+
if (!isWidgetPackageCssArtifactPath(value)) errors.push({
|
|
2500
2531
|
code: "INVALID_SOURCE_PACKAGE",
|
|
2501
2532
|
path: cssUrlPath,
|
|
2502
|
-
message: "Widget package
|
|
2533
|
+
message: "Widget package cssUrls entries must be top-level generated CSS artifact filenames matching [A-Za-z0-9._~-]+.css. Self-host external CSS or fonts via @font-face in widget CSS so the build can isolate them."
|
|
2503
2534
|
});
|
|
2504
2535
|
}
|
|
2505
2536
|
function validateRuntimeDescriptorUrl(value, urlPath) {
|
|
@@ -2583,21 +2614,19 @@ function validateOptionalRecordField(record, key, path, errors) {
|
|
|
2583
2614
|
}
|
|
2584
2615
|
function mergeCssUrls(sourceCssUrls, generatedCssUrls) {
|
|
2585
2616
|
const generatedCssUrlSet = new Set(generatedCssUrls ?? []);
|
|
2617
|
+
generatedCssUrls?.forEach((cssUrl, index) => {
|
|
2618
|
+
validateGeneratedCssArtifactUrl(cssUrl, `generated cssUrls[${index}]`);
|
|
2619
|
+
});
|
|
2586
2620
|
sourceCssUrls?.forEach((cssUrl, index) => {
|
|
2587
|
-
|
|
2588
|
-
if (isAbsoluteRuntimeUrl(cssUrl)) return;
|
|
2589
|
-
if (!isWidgetPackageCssArtifactPath(cssUrl)) throw new Error(`cssUrls[${index}] relative URL ${JSON.stringify(cssUrl)} must be a top-level CSS artifact filename matching [A-Za-z0-9._~-]+.css.`);
|
|
2621
|
+
validateGeneratedCssArtifactUrl(cssUrl, `cssUrls[${index}]`);
|
|
2590
2622
|
if (generatedCssUrlSet.has(cssUrl)) return;
|
|
2591
|
-
throw new Error(`cssUrls[${index}]
|
|
2592
|
-
});
|
|
2593
|
-
const cssUrls = Array.from(new Set([...sourceCssUrls ?? [], ...generatedCssUrls ?? []]));
|
|
2594
|
-
cssUrls.forEach((cssUrl, index) => {
|
|
2595
|
-
validateRuntimeDescriptorUrl(cssUrl, `cssUrls[${index}]`);
|
|
2623
|
+
throw new Error(`cssUrls[${index}] URL ${JSON.stringify(cssUrl)} must match a generated top-level CSS artifact. Import the CSS from the widget package bundle or remove it from cssUrls.`);
|
|
2596
2624
|
});
|
|
2597
|
-
return
|
|
2625
|
+
return Array.from(new Set([...sourceCssUrls ?? [], ...generatedCssUrls ?? []]));
|
|
2598
2626
|
}
|
|
2599
|
-
function
|
|
2600
|
-
|
|
2627
|
+
function validateGeneratedCssArtifactUrl(value, cssUrlPath) {
|
|
2628
|
+
if (value.trim().length === 0) throw new Error(`${cssUrlPath} must be a non-empty string.`);
|
|
2629
|
+
if (!isWidgetPackageCssArtifactPath(value)) throw new Error(`${cssUrlPath} must be a top-level generated CSS artifact filename matching [A-Za-z0-9._~-]+.css. Self-host external CSS or fonts via @font-face in widget CSS so the build can isolate them.`);
|
|
2601
2630
|
}
|
|
2602
2631
|
function normalizePropertySchema(value, widgetType) {
|
|
2603
2632
|
return {
|
|
@@ -2767,9 +2796,13 @@ async function buildSharedWidgetPackage(options) {
|
|
|
2767
2796
|
}
|
|
2768
2797
|
}
|
|
2769
2798
|
function createWidgetPackageEntrySource(options) {
|
|
2799
|
+
const runtimePackageId = getWidgetPackageDescriptorPackageId(options.validated);
|
|
2770
2800
|
return `import * as widgetConfig from ${JSON.stringify(options.configImportPath)};
|
|
2801
|
+
import { createElement } from "react";
|
|
2771
2802
|
|
|
2772
2803
|
const SOURCE_PACKAGE_MARKER = "__fluidSourceWidgetPackage";
|
|
2804
|
+
const WIDGET_PACKAGE_ID = ${JSON.stringify(runtimePackageId)};
|
|
2805
|
+
const WIDGET_PACKAGE_VERSION = ${JSON.stringify(options.validated.version)};
|
|
2773
2806
|
const descriptors = ${JSON.stringify(options.validated.widgets, null, 2)};
|
|
2774
2807
|
|
|
2775
2808
|
function isSourceWidgetPackage(value) {
|
|
@@ -2793,12 +2826,31 @@ const componentsByName = new Map(
|
|
|
2793
2826
|
sourcePackage.widgets.map((widget) => [widget.name, widget.component]),
|
|
2794
2827
|
);
|
|
2795
2828
|
|
|
2829
|
+
function createScopedWidgetComponent(component, descriptor) {
|
|
2830
|
+
const wrapperTagName = descriptor.container === "inline" ? "span" : "div";
|
|
2831
|
+
const WrappedFluidWidgetComponent = (props) => createElement(
|
|
2832
|
+
wrapperTagName,
|
|
2833
|
+
{
|
|
2834
|
+
"data-fluid-widget-package": WIDGET_PACKAGE_ID,
|
|
2835
|
+
"data-fluid-widget-version": WIDGET_PACKAGE_VERSION,
|
|
2836
|
+
"data-fluid-widget-name": descriptor.name,
|
|
2837
|
+
},
|
|
2838
|
+
// PortalContainerProvider is not imported here because standalone widget packages
|
|
2839
|
+
// only depend on @fluid-app/portal-sdk, which does not currently expose a
|
|
2840
|
+
// public portal-container provider. Radix/ui-primitives portals remain a
|
|
2841
|
+
// residual isolation risk until a public widget-safe provider is available.
|
|
2842
|
+
createElement(component, props),
|
|
2843
|
+
);
|
|
2844
|
+
WrappedFluidWidgetComponent.displayName = "FluidWidgetPackage(" + WIDGET_PACKAGE_ID + "/" + descriptor.name + ")";
|
|
2845
|
+
return WrappedFluidWidgetComponent;
|
|
2846
|
+
}
|
|
2847
|
+
|
|
2796
2848
|
const widgets = descriptors.map((descriptor) => {
|
|
2797
2849
|
const component = componentsByName.get(descriptor.name);
|
|
2798
2850
|
if (typeof component !== "function" && typeof component !== "object") {
|
|
2799
2851
|
throw new Error("Widget package is missing component for " + descriptor.name + ".");
|
|
2800
2852
|
}
|
|
2801
|
-
return { ...descriptor, component };
|
|
2853
|
+
return { ...descriptor, component: createScopedWidgetComponent(component, descriptor) };
|
|
2802
2854
|
});
|
|
2803
2855
|
|
|
2804
2856
|
var _fluidWidgets = globalObject.FluidWidgets;
|
|
@@ -2807,7 +2859,7 @@ if (!_fluidWidgets || typeof _fluidWidgets.registerPackage !== "function") {
|
|
|
2807
2859
|
}
|
|
2808
2860
|
|
|
2809
2861
|
_fluidWidgets.registerPackage({
|
|
2810
|
-
packageId: ${JSON.stringify(
|
|
2862
|
+
packageId: ${JSON.stringify(runtimePackageId)},
|
|
2811
2863
|
version: ${JSON.stringify(options.validated.version)},
|
|
2812
2864
|
widgets,
|
|
2813
2865
|
});
|
|
@@ -2824,6 +2876,8 @@ async function buildWidgetScriptBundle(options) {
|
|
|
2824
2876
|
}), "utf-8");
|
|
2825
2877
|
await fs.writeFile(viteConfigPath, createViteConfigSource({
|
|
2826
2878
|
entryPath,
|
|
2879
|
+
packageId: getWidgetPackageDescriptorPackageId(options.validated),
|
|
2880
|
+
packageVersion: options.validated.version,
|
|
2827
2881
|
publishDir: options.publishDir,
|
|
2828
2882
|
projectConfigPath: await resolveProjectViteConfigPath(options.projectDir)
|
|
2829
2883
|
}), "utf-8");
|
|
@@ -2950,6 +3004,21 @@ export default defineConfig(async (configEnv) => {
|
|
|
2950
3004
|
function createWidgetViteConfigObjectSource(options) {
|
|
2951
3005
|
const configFileSource = options.disableConfigFileLookup ? " configFile: false,\n" : "";
|
|
2952
3006
|
return `(() => {
|
|
3007
|
+
const widgetPackageId = ${JSON.stringify(options.packageId)};
|
|
3008
|
+
const widgetPackageVersion = ${JSON.stringify(options.packageVersion)};
|
|
3009
|
+
const widgetPackageScopeSelector = '[data-fluid-widget-package="' + widgetPackageId + '"][data-fluid-widget-version="' + widgetPackageVersion + '"]';
|
|
3010
|
+
const widgetPackageGeneratedCssFileName = "widget.css";
|
|
3011
|
+
const widgetPackageKeyframePrefix = "fluid-widget-" + encodeCssIdentifier(widgetPackageId) + "-" + encodeCssIdentifier(widgetPackageVersion) + "-";
|
|
3012
|
+
const widgetPackageLayerPrefix = widgetPackageKeyframePrefix + "layer-";
|
|
3013
|
+
const widgetPackageHostDefaultLayerName = widgetPackageLayerPrefix + "generated-host-defaults";
|
|
3014
|
+
const widgetPackageHostDefaultRule = "@layer " + widgetPackageHostDefaultLayerName + " { " + widgetPackageScopeSelector + " { display: contents; } }";
|
|
3015
|
+
const widgetPackageContainerPrefix = widgetPackageKeyframePrefix + "container-";
|
|
3016
|
+
const widgetPackageCssGlobalNamePrefix = widgetPackageKeyframePrefix + "css-name-";
|
|
3017
|
+
const groupingAtRules = new Set(["media", "supports", "container", "document", "starting-style"]);
|
|
3018
|
+
const unsafeSelectorPattern = /(^|[\\s>+~,])(:root|html|body)(?=$|[\\s.#:[>+~,)])/i;
|
|
3019
|
+
const universalSelectorPattern = /(^|[\\s>+~,])\\*(?=$|[\\s.#:[>+~,)])/g;
|
|
3020
|
+
const pseudoElementOnlySelectorPattern = /^::[A-Za-z-]+(?:\\([^)]*\\))?$/;
|
|
3021
|
+
|
|
2953
3022
|
function createFluidWidgetBuildInvariants() {
|
|
2954
3023
|
return {
|
|
2955
3024
|
copyPublicDir: false,
|
|
@@ -2988,12 +3057,1169 @@ function createWidgetViteConfigObjectSource(options) {
|
|
|
2988
3057
|
};
|
|
2989
3058
|
}
|
|
2990
3059
|
|
|
3060
|
+
function fluidWidgetCssIsolationPlugin() {
|
|
3061
|
+
return {
|
|
3062
|
+
name: "fluid-widget-css-isolation",
|
|
3063
|
+
enforce: "post",
|
|
3064
|
+
generateBundle(_outputOptions, bundle) {
|
|
3065
|
+
let hasCssAsset = false;
|
|
3066
|
+
for (const asset of Object.values(bundle)) {
|
|
3067
|
+
if (!asset || asset.type !== "asset" || !asset.fileName.endsWith(".css")) continue;
|
|
3068
|
+
hasCssAsset = true;
|
|
3069
|
+
const source = typeof asset.source === "string" ? asset.source : new TextDecoder().decode(asset.source);
|
|
3070
|
+
asset.source = isolateWidgetCssAsset(source, asset.fileName);
|
|
3071
|
+
}
|
|
3072
|
+
if (!hasCssAsset) {
|
|
3073
|
+
this.emitFile({
|
|
3074
|
+
type: "asset",
|
|
3075
|
+
fileName: widgetPackageGeneratedCssFileName,
|
|
3076
|
+
source: widgetPackageHostDefaultRule + "\\n",
|
|
3077
|
+
});
|
|
3078
|
+
}
|
|
3079
|
+
},
|
|
3080
|
+
};
|
|
3081
|
+
}
|
|
3082
|
+
|
|
3083
|
+
function isolateWidgetCssAsset(css, assetName) {
|
|
3084
|
+
const keyframeNames = collectKeyframeNames(css);
|
|
3085
|
+
const fontFamilyNames = collectFontFamilyNames(css);
|
|
3086
|
+
const customPropertyNames = collectRegisteredCustomPropertyNames(css);
|
|
3087
|
+
const placementSensitivePrefix = readPlacementSensitiveCssPrefix(css, assetName);
|
|
3088
|
+
const isolatedCss = isolateCssRange(css, assetName, keyframeNames, fontFamilyNames, customPropertyNames, placementSensitivePrefix.endIndex, css.length).css;
|
|
3089
|
+
return placementSensitivePrefix.css + widgetPackageHostDefaultRule + "\\n" + isolatedCss;
|
|
3090
|
+
}
|
|
3091
|
+
|
|
3092
|
+
function collectKeyframeNames(css) {
|
|
3093
|
+
const names = new Map();
|
|
3094
|
+
visitCssAtRules(css, (atRuleName, atRule) => {
|
|
3095
|
+
if (!isKeyframesAtRule(atRuleName)) return false;
|
|
3096
|
+
const match = /^@(?:-[A-Za-z]+-)?keyframes\\s+([^\\s{]+)/i.exec(atRule.prelude);
|
|
3097
|
+
const name = match?.[1];
|
|
3098
|
+
if (name && !names.has(name)) names.set(name, widgetPackageKeyframePrefix + name);
|
|
3099
|
+
return atRule.blockStart !== -1;
|
|
3100
|
+
});
|
|
3101
|
+
return names;
|
|
3102
|
+
}
|
|
3103
|
+
|
|
3104
|
+
function collectFontFamilyNames(css) {
|
|
3105
|
+
const names = new Map();
|
|
3106
|
+
visitCssAtRules(css, (atRuleName, atRule) => {
|
|
3107
|
+
if (atRuleName !== "font-face" || atRule.blockStart === -1 || atRule.blockEnd === -1) return false;
|
|
3108
|
+
let familyName = "";
|
|
3109
|
+
rewriteDeclarationBlock(css.slice(atRule.blockStart + 1, atRule.blockEnd), (propertyName, rawProperty, value) => {
|
|
3110
|
+
if (propertyName === "font-family" && !familyName) {
|
|
3111
|
+
familyName = normalizeFontFamilyName(value);
|
|
3112
|
+
}
|
|
3113
|
+
return { property: rawProperty, value };
|
|
3114
|
+
});
|
|
3115
|
+
if (familyName && !names.has(familyName)) names.set(familyName, widgetPackageKeyframePrefix + familyName);
|
|
3116
|
+
return true;
|
|
3117
|
+
});
|
|
3118
|
+
return names;
|
|
3119
|
+
}
|
|
3120
|
+
|
|
3121
|
+
function collectRegisteredCustomPropertyNames(css) {
|
|
3122
|
+
const names = new Map();
|
|
3123
|
+
visitCssAtRules(css, (atRuleName, atRule) => {
|
|
3124
|
+
if (atRuleName !== "property") return false;
|
|
3125
|
+
const match = /^@property\\s+(--[A-Za-z0-9_-]+)/i.exec(atRule.prelude);
|
|
3126
|
+
const name = match?.[1];
|
|
3127
|
+
if (name && !names.has(name)) names.set(name, "--" + widgetPackageKeyframePrefix + name.slice(2));
|
|
3128
|
+
return atRule.blockStart !== -1;
|
|
3129
|
+
});
|
|
3130
|
+
return names;
|
|
3131
|
+
}
|
|
3132
|
+
|
|
3133
|
+
function visitCssAtRules(css, visitAtRule) {
|
|
3134
|
+
let index = 0;
|
|
3135
|
+
while (index < css.length) {
|
|
3136
|
+
if (cssStartsWithComment(css, index)) {
|
|
3137
|
+
const commentEnd = css.indexOf("*/", index + 2);
|
|
3138
|
+
index = commentEnd === -1 ? css.length : commentEnd + 2;
|
|
3139
|
+
continue;
|
|
3140
|
+
}
|
|
3141
|
+
const char = css[index];
|
|
3142
|
+
if (char === '"' || char === "'") {
|
|
3143
|
+
index = findCssStringEnd(css, index);
|
|
3144
|
+
continue;
|
|
3145
|
+
}
|
|
3146
|
+
if (char !== "@") {
|
|
3147
|
+
index += 1;
|
|
3148
|
+
continue;
|
|
3149
|
+
}
|
|
3150
|
+
const atRule = readRulePrelude(css, index, css.length);
|
|
3151
|
+
const blockEnd = atRule.blockStart === -1 ? -1 : findMatchingBrace(css, atRule.blockStart, css.length);
|
|
3152
|
+
const shouldSkipBlock = visitAtRule(readAtRuleName(atRule.prelude), { ...atRule, blockEnd });
|
|
3153
|
+
index = shouldSkipBlock && blockEnd !== -1 ? blockEnd + 1 : index + 1;
|
|
3154
|
+
}
|
|
3155
|
+
}
|
|
3156
|
+
|
|
3157
|
+
function isolateCssRange(css, assetName, keyframeNames, fontFamilyNames, customPropertyNames, startIndex, endIndex) {
|
|
3158
|
+
let output = "";
|
|
3159
|
+
let index = startIndex;
|
|
3160
|
+
while (index < endIndex) {
|
|
3161
|
+
if (css.startsWith("/*", index)) {
|
|
3162
|
+
const commentEnd = css.indexOf("*/", index + 2);
|
|
3163
|
+
const nextIndex = commentEnd === -1 ? endIndex : commentEnd + 2;
|
|
3164
|
+
output += css.slice(index, nextIndex);
|
|
3165
|
+
index = nextIndex;
|
|
3166
|
+
continue;
|
|
3167
|
+
}
|
|
3168
|
+
|
|
3169
|
+
if (/\\s/.test(css[index] ?? "")) {
|
|
3170
|
+
output += css[index];
|
|
3171
|
+
index += 1;
|
|
3172
|
+
continue;
|
|
3173
|
+
}
|
|
3174
|
+
|
|
3175
|
+
if (css[index] === "@") {
|
|
3176
|
+
const atRule = readRulePrelude(css, index, endIndex);
|
|
3177
|
+
const atRuleName = readAtRuleName(atRule.prelude);
|
|
3178
|
+
if (atRule.blockStart === -1) {
|
|
3179
|
+
if (atRuleName === "import") {
|
|
3180
|
+
throw new Error('Unsupported @import rule in CSS asset "' + assetName + '" cannot be isolated safely. Self-host external CSS or fonts via @font-face in widget CSS so the build can isolate them.');
|
|
3181
|
+
}
|
|
3182
|
+
if (atRuleName === "charset" || atRuleName === "namespace") {
|
|
3183
|
+
throw new Error('Unsupported @' + atRuleName + ' rule in CSS asset "' + assetName + '" cannot be isolated safely after generated host defaults. Move @' + atRuleName + ' before all non-@charset/@namespace rules.');
|
|
3184
|
+
}
|
|
3185
|
+
if (atRuleName === "layer") {
|
|
3186
|
+
output += rewriteLayerPrelude(atRule.prelude) + css.slice(index + atRule.prelude.length, atRule.end);
|
|
3187
|
+
index = atRule.end;
|
|
3188
|
+
continue;
|
|
3189
|
+
}
|
|
3190
|
+
output += css.slice(index, atRule.end);
|
|
3191
|
+
index = atRule.end;
|
|
3192
|
+
continue;
|
|
3193
|
+
}
|
|
3194
|
+
|
|
3195
|
+
const blockEnd = findMatchingBrace(css, atRule.blockStart, endIndex);
|
|
3196
|
+
if (blockEnd === -1) {
|
|
3197
|
+
output += css.slice(index, endIndex);
|
|
3198
|
+
index = endIndex;
|
|
3199
|
+
continue;
|
|
3200
|
+
}
|
|
3201
|
+
|
|
3202
|
+
const ruleHeader = css.slice(index, atRule.blockStart + 1);
|
|
3203
|
+
if (isKeyframesAtRule(atRuleName)) {
|
|
3204
|
+
output += rewriteKeyframesHeader(ruleHeader, keyframeNames) + namespaceKeyframesBlock(css.slice(atRule.blockStart + 1, blockEnd), keyframeNames, fontFamilyNames, customPropertyNames, assetName) + "}";
|
|
3205
|
+
} else if (atRuleName === "font-face") {
|
|
3206
|
+
output += ruleHeader + namespaceFontFaceBlock(css.slice(atRule.blockStart + 1, blockEnd), fontFamilyNames, customPropertyNames) + "}";
|
|
3207
|
+
} else if (atRuleName === "property") {
|
|
3208
|
+
output += rewritePropertyHeader(ruleHeader, customPropertyNames) + namespaceCustomPropertyReferences(css.slice(atRule.blockStart + 1, blockEnd), customPropertyNames) + "}";
|
|
3209
|
+
} else if (atRuleName === "layer") {
|
|
3210
|
+
output += rewriteLayerPrelude(atRule.prelude) + css.slice(index + atRule.prelude.length, atRule.blockStart + 1) + isolateCssRange(css, assetName, keyframeNames, fontFamilyNames, customPropertyNames, atRule.blockStart + 1, blockEnd).css + "}";
|
|
3211
|
+
} else if (groupingAtRules.has(atRuleName)) {
|
|
3212
|
+
const groupingRuleHeader = atRuleName === "container" ? rewriteContainerAtRuleHeader(ruleHeader) : ruleHeader;
|
|
3213
|
+
output += groupingRuleHeader + isolateCssRange(css, assetName, keyframeNames, fontFamilyNames, customPropertyNames, atRule.blockStart + 1, blockEnd).css + "}";
|
|
3214
|
+
} else {
|
|
3215
|
+
throw new Error('Unsupported @' + atRuleName + ' rule in CSS asset "' + assetName + '" cannot be isolated safely.');
|
|
3216
|
+
}
|
|
3217
|
+
index = blockEnd + 1;
|
|
3218
|
+
continue;
|
|
3219
|
+
}
|
|
3220
|
+
|
|
3221
|
+
const styleRule = readRulePrelude(css, index, endIndex);
|
|
3222
|
+
if (styleRule.blockStart === -1) {
|
|
3223
|
+
output += css.slice(index, styleRule.end);
|
|
3224
|
+
index = styleRule.end;
|
|
3225
|
+
continue;
|
|
3226
|
+
}
|
|
3227
|
+
|
|
3228
|
+
const styleBlockEnd = findMatchingBrace(css, styleRule.blockStart, endIndex);
|
|
3229
|
+
if (styleBlockEnd === -1) {
|
|
3230
|
+
output += css.slice(index, endIndex);
|
|
3231
|
+
index = endIndex;
|
|
3232
|
+
continue;
|
|
3233
|
+
}
|
|
3234
|
+
|
|
3235
|
+
output += isolateSelectorList(styleRule.prelude, assetName) + "{" + namespaceStyleBlock(css.slice(styleRule.blockStart + 1, styleBlockEnd), keyframeNames, fontFamilyNames, customPropertyNames, assetName) + "}";
|
|
3236
|
+
index = styleBlockEnd + 1;
|
|
3237
|
+
}
|
|
3238
|
+
|
|
3239
|
+
return { css: output, index };
|
|
3240
|
+
}
|
|
3241
|
+
|
|
3242
|
+
function readPlacementSensitiveCssPrefix(css, assetName) {
|
|
3243
|
+
let output = "";
|
|
3244
|
+
let index = 0;
|
|
3245
|
+
let hasNamespace = false;
|
|
3246
|
+
while (index < css.length) {
|
|
3247
|
+
const leadingStart = index;
|
|
3248
|
+
index = skipCssWhitespaceAndComments(css, index);
|
|
3249
|
+
const leading = css.slice(leadingStart, index);
|
|
3250
|
+
if (css[index] !== "@") {
|
|
3251
|
+
return { css: output + leading, endIndex: index };
|
|
3252
|
+
}
|
|
3253
|
+
|
|
3254
|
+
const atRule = readRulePrelude(css, index, css.length);
|
|
3255
|
+
const atRuleName = readAtRuleName(atRule.prelude);
|
|
3256
|
+
if (atRuleName !== "charset" && atRuleName !== "namespace") {
|
|
3257
|
+
return { css: output + leading, endIndex: index };
|
|
3258
|
+
}
|
|
3259
|
+
if (atRule.blockStart !== -1) {
|
|
3260
|
+
throw new Error('Unsupported @' + atRuleName + ' rule in CSS asset "' + assetName + '" cannot be isolated safely.');
|
|
3261
|
+
}
|
|
3262
|
+
if (atRuleName === "charset" && hasNamespace) {
|
|
3263
|
+
throw new Error('Unsupported @charset rule in CSS asset "' + assetName + '" cannot be isolated safely after @namespace. Move @charset before @namespace.');
|
|
3264
|
+
}
|
|
3265
|
+
if (atRuleName === "namespace") hasNamespace = true;
|
|
3266
|
+
output += leading + css.slice(index, atRule.end);
|
|
3267
|
+
index = atRule.end;
|
|
3268
|
+
}
|
|
3269
|
+
return { css: output, endIndex: index };
|
|
3270
|
+
}
|
|
3271
|
+
|
|
3272
|
+
function readRulePrelude(css, startIndex, endIndex) {
|
|
3273
|
+
let index = startIndex;
|
|
3274
|
+
let quote = "";
|
|
3275
|
+
let bracketDepth = 0;
|
|
3276
|
+
let parenDepth = 0;
|
|
3277
|
+
while (index < endIndex) {
|
|
3278
|
+
const char = css[index];
|
|
3279
|
+
if (quote) {
|
|
3280
|
+
if (char === "\\\\") index += 2;
|
|
3281
|
+
else {
|
|
3282
|
+
if (char === quote) quote = "";
|
|
3283
|
+
index += 1;
|
|
3284
|
+
}
|
|
3285
|
+
continue;
|
|
3286
|
+
}
|
|
3287
|
+
if (cssStartsWithComment(css, index)) {
|
|
3288
|
+
const commentEnd = css.indexOf("*/", index + 2);
|
|
3289
|
+
index = commentEnd === -1 ? endIndex : Math.min(commentEnd + 2, endIndex);
|
|
3290
|
+
continue;
|
|
3291
|
+
}
|
|
3292
|
+
if (char === '"' || char === "'") {
|
|
3293
|
+
quote = char;
|
|
3294
|
+
index += 1;
|
|
3295
|
+
continue;
|
|
3296
|
+
}
|
|
3297
|
+
if (char === "[") bracketDepth += 1;
|
|
3298
|
+
else if (char === "]") bracketDepth = Math.max(0, bracketDepth - 1);
|
|
3299
|
+
else if (char === "(") parenDepth += 1;
|
|
3300
|
+
else if (char === ")") parenDepth = Math.max(0, parenDepth - 1);
|
|
3301
|
+
else if (bracketDepth === 0 && parenDepth === 0 && char === "{") {
|
|
3302
|
+
return { prelude: css.slice(startIndex, index), blockStart: index, end: index + 1 };
|
|
3303
|
+
} else if (bracketDepth === 0 && parenDepth === 0 && char === ";") {
|
|
3304
|
+
return { prelude: css.slice(startIndex, index), blockStart: -1, end: index + 1 };
|
|
3305
|
+
}
|
|
3306
|
+
index += 1;
|
|
3307
|
+
}
|
|
3308
|
+
return { prelude: css.slice(startIndex, endIndex), blockStart: -1, end: endIndex };
|
|
3309
|
+
}
|
|
3310
|
+
|
|
3311
|
+
function findMatchingBrace(css, openBraceIndex, endIndex) {
|
|
3312
|
+
let depth = 1;
|
|
3313
|
+
let index = openBraceIndex + 1;
|
|
3314
|
+
let quote = "";
|
|
3315
|
+
while (index < endIndex) {
|
|
3316
|
+
const char = css[index];
|
|
3317
|
+
if (quote) {
|
|
3318
|
+
if (char === "\\\\") index += 2;
|
|
3319
|
+
else {
|
|
3320
|
+
if (char === quote) quote = "";
|
|
3321
|
+
index += 1;
|
|
3322
|
+
}
|
|
3323
|
+
continue;
|
|
3324
|
+
}
|
|
3325
|
+
if (char === '"' || char === "'") {
|
|
3326
|
+
quote = char;
|
|
3327
|
+
index += 1;
|
|
3328
|
+
continue;
|
|
3329
|
+
}
|
|
3330
|
+
if (css.startsWith("/*", index)) {
|
|
3331
|
+
const commentEnd = css.indexOf("*/", index + 2);
|
|
3332
|
+
index = commentEnd === -1 ? endIndex : commentEnd + 2;
|
|
3333
|
+
continue;
|
|
3334
|
+
}
|
|
3335
|
+
if (char === "{") depth += 1;
|
|
3336
|
+
else if (char === "}") {
|
|
3337
|
+
depth -= 1;
|
|
3338
|
+
if (depth === 0) return index;
|
|
3339
|
+
}
|
|
3340
|
+
index += 1;
|
|
3341
|
+
}
|
|
3342
|
+
return -1;
|
|
3343
|
+
}
|
|
3344
|
+
|
|
3345
|
+
function readAtRuleName(prelude) {
|
|
3346
|
+
const match = /^@([A-Za-z-]+)/.exec(prelude.trim());
|
|
3347
|
+
return match?.[1]?.toLowerCase() ?? "";
|
|
3348
|
+
}
|
|
3349
|
+
|
|
3350
|
+
function isKeyframesAtRule(atRuleName) {
|
|
3351
|
+
return atRuleName === "keyframes" || atRuleName.endsWith("-keyframes");
|
|
3352
|
+
}
|
|
3353
|
+
|
|
3354
|
+
function rewriteKeyframesHeader(header, keyframeNames) {
|
|
3355
|
+
return header.replace(/(@(?:-[A-Za-z]+-)?keyframes\\s+)([^\\s{]+)/, (_match, prefix, name) => prefix + (keyframeNames.get(name) ?? name));
|
|
3356
|
+
}
|
|
3357
|
+
|
|
3358
|
+
function splitSelectorList(selectorList) {
|
|
3359
|
+
const selectors = [];
|
|
3360
|
+
let current = "";
|
|
3361
|
+
let quote = "";
|
|
3362
|
+
let bracketDepth = 0;
|
|
3363
|
+
let parenDepth = 0;
|
|
3364
|
+
for (let index = 0; index < selectorList.length; index += 1) {
|
|
3365
|
+
const char = selectorList[index];
|
|
3366
|
+
if (quote) {
|
|
3367
|
+
current += char;
|
|
3368
|
+
if (char === "\\\\") {
|
|
3369
|
+
index += 1;
|
|
3370
|
+
current += selectorList[index] ?? "";
|
|
3371
|
+
} else if (char === quote) quote = "";
|
|
3372
|
+
continue;
|
|
3373
|
+
}
|
|
3374
|
+
if (char === '"' || char === "'") {
|
|
3375
|
+
quote = char;
|
|
3376
|
+
current += char;
|
|
3377
|
+
continue;
|
|
3378
|
+
}
|
|
3379
|
+
if (cssStartsWithComment(selectorList, index)) {
|
|
3380
|
+
const commentEnd = selectorList.indexOf("*/", index + 2);
|
|
3381
|
+
const nextIndex = commentEnd === -1 ? selectorList.length : commentEnd + 2;
|
|
3382
|
+
current += selectorList.slice(index, nextIndex);
|
|
3383
|
+
index = nextIndex - 1;
|
|
3384
|
+
continue;
|
|
3385
|
+
}
|
|
3386
|
+
if (char === "[") bracketDepth += 1;
|
|
3387
|
+
else if (char === "]") bracketDepth = Math.max(0, bracketDepth - 1);
|
|
3388
|
+
else if (char === "(") parenDepth += 1;
|
|
3389
|
+
else if (char === ")") parenDepth = Math.max(0, parenDepth - 1);
|
|
3390
|
+
if (char === "," && bracketDepth === 0 && parenDepth === 0) {
|
|
3391
|
+
selectors.push(current);
|
|
3392
|
+
current = "";
|
|
3393
|
+
} else current += char;
|
|
3394
|
+
}
|
|
3395
|
+
selectors.push(current);
|
|
3396
|
+
return selectors;
|
|
3397
|
+
}
|
|
3398
|
+
|
|
3399
|
+
function isolateSelectorList(selectorList, assetName) {
|
|
3400
|
+
return splitSelectorList(selectorList).map((selector) => isolateSelector(selector, assetName)).join(",");
|
|
3401
|
+
}
|
|
3402
|
+
|
|
3403
|
+
function isolateSelector(selector, assetName) {
|
|
3404
|
+
const leadingWhitespace = selector.match(/^\\s*/)?.[0] ?? "";
|
|
3405
|
+
const trailingWhitespace = selector.match(/\\s*$/)?.[0] ?? "";
|
|
3406
|
+
const trimmed = selector.trim();
|
|
3407
|
+
assertSafeSelector(trimmed, assetName);
|
|
3408
|
+
if (trimmed.includes(":host")) {
|
|
3409
|
+
return leadingWhitespace + isolateHostSelector(trimmed, assetName) + trailingWhitespace;
|
|
3410
|
+
}
|
|
3411
|
+
return leadingWhitespace + widgetPackageScopeSelector + " " + trimmed + trailingWhitespace;
|
|
3412
|
+
}
|
|
3413
|
+
|
|
3414
|
+
function isolateHostSelector(selector, assetName) {
|
|
3415
|
+
if (!selector.startsWith(":host")) {
|
|
3416
|
+
throw new Error('Unsupported :host selector "' + selector + '" in CSS asset "' + assetName + '" cannot be isolated safely.');
|
|
3417
|
+
}
|
|
3418
|
+
const match = /^:host(?:\\(([^()]*)\\))?($|[\\s>+~].*)/.exec(selector);
|
|
3419
|
+
if (!match) {
|
|
3420
|
+
throw new Error('Unsupported :host selector "' + selector + '" in CSS asset "' + assetName + '" cannot be isolated safely.');
|
|
3421
|
+
}
|
|
3422
|
+
const hostSelector = (match[1] ?? "").trim();
|
|
3423
|
+
const selectorAfterHost = match[2] ?? "";
|
|
3424
|
+
const firstSelectorAfterHostIndex = skipCssWhitespaceAndComments(selectorAfterHost, 0);
|
|
3425
|
+
const firstSelectorAfterHostChar = selectorAfterHost[firstSelectorAfterHostIndex] ?? "";
|
|
3426
|
+
if (hostSelector.includes(",") || hasTopLevelHostArgumentCombinator(hostSelector) || firstSelectorAfterHostChar === "+" || firstSelectorAfterHostChar === "~" || (hostSelector && (unsafeSelectorPattern.test(hostSelector) || hasUnsafeUniversalSelector(hostSelector)))) {
|
|
3427
|
+
throw new Error('Unsupported :host selector "' + selector + '" in CSS asset "' + assetName + '" cannot be isolated safely.');
|
|
3428
|
+
}
|
|
3429
|
+
return widgetPackageScopeSelector + hostSelector + selectorAfterHost;
|
|
3430
|
+
}
|
|
3431
|
+
|
|
3432
|
+
function hasTopLevelHostArgumentCombinator(value) {
|
|
3433
|
+
let quote = "";
|
|
3434
|
+
let bracketDepth = 0;
|
|
3435
|
+
let hasSelectorBeforeWhitespace = false;
|
|
3436
|
+
let index = 0;
|
|
3437
|
+
while (index < value.length) {
|
|
3438
|
+
const char = value[index];
|
|
3439
|
+
if (quote) {
|
|
3440
|
+
if (char === "\\\\") index += 2;
|
|
3441
|
+
else {
|
|
3442
|
+
if (char === quote) quote = "";
|
|
3443
|
+
index += 1;
|
|
3444
|
+
}
|
|
3445
|
+
continue;
|
|
3446
|
+
}
|
|
3447
|
+
if (char === '"' || char === "'") {
|
|
3448
|
+
quote = char;
|
|
3449
|
+
index += 1;
|
|
3450
|
+
continue;
|
|
3451
|
+
}
|
|
3452
|
+
if (char === "[") {
|
|
3453
|
+
bracketDepth += 1;
|
|
3454
|
+
hasSelectorBeforeWhitespace = true;
|
|
3455
|
+
} else if (char === "]") {
|
|
3456
|
+
bracketDepth = Math.max(0, bracketDepth - 1);
|
|
3457
|
+
} else if (bracketDepth === 0 && (char === "+" || char === "~" || char === ">")) {
|
|
3458
|
+
return true;
|
|
3459
|
+
} else if (bracketDepth === 0 && /\\s/.test(char)) {
|
|
3460
|
+
const nextIndex = skipCssWhitespaceAndComments(value, index);
|
|
3461
|
+
if (hasSelectorBeforeWhitespace && nextIndex < value.length) return true;
|
|
3462
|
+
index = nextIndex;
|
|
3463
|
+
continue;
|
|
3464
|
+
} else if (bracketDepth === 0 && cssStartsWithComment(value, index)) {
|
|
3465
|
+
const nextIndex = skipCssWhitespaceAndComments(value, index);
|
|
3466
|
+
if (hasSelectorBeforeWhitespace && nextIndex < value.length) return true;
|
|
3467
|
+
index = nextIndex;
|
|
3468
|
+
continue;
|
|
3469
|
+
} else if (bracketDepth === 0 && !/\\s/.test(char)) {
|
|
3470
|
+
hasSelectorBeforeWhitespace = true;
|
|
3471
|
+
}
|
|
3472
|
+
index += 1;
|
|
3473
|
+
}
|
|
3474
|
+
return false;
|
|
3475
|
+
}
|
|
3476
|
+
|
|
3477
|
+
function assertSafeSelector(selector, assetName) {
|
|
3478
|
+
if (!selector || unsafeSelectorPattern.test(selector) || hasUnsafeUniversalSelector(selector) || pseudoElementOnlySelectorPattern.test(selector)) {
|
|
3479
|
+
throw new Error('Unsafe selector "' + selector + '" in CSS asset "' + assetName + '" cannot be isolated safely.');
|
|
3480
|
+
}
|
|
3481
|
+
}
|
|
3482
|
+
|
|
3483
|
+
function hasUnsafeUniversalSelector(selector) {
|
|
3484
|
+
universalSelectorPattern.lastIndex = 0;
|
|
3485
|
+
let match;
|
|
3486
|
+
while ((match = universalSelectorPattern.exec(selector)) !== null) {
|
|
3487
|
+
const universalStart = match.index + match[1].length;
|
|
3488
|
+
if (!hasAnchoringSelectorBefore(selector.slice(0, universalStart))) return true;
|
|
3489
|
+
}
|
|
3490
|
+
return false;
|
|
3491
|
+
}
|
|
3492
|
+
|
|
3493
|
+
function hasAnchoringSelectorBefore(value) {
|
|
3494
|
+
return stripCssComments(value).replace(/[\\s>+~]+/g, "").length > 0;
|
|
3495
|
+
}
|
|
3496
|
+
|
|
3497
|
+
function namespaceKeyframesBlock(block, keyframeNames, fontFamilyNames, customPropertyNames, assetName) {
|
|
3498
|
+
let output = "";
|
|
3499
|
+
let index = 0;
|
|
3500
|
+
while (index < block.length) {
|
|
3501
|
+
if (cssStartsWithComment(block, index)) {
|
|
3502
|
+
const commentEnd = block.indexOf("*/", index + 2);
|
|
3503
|
+
const nextIndex = commentEnd === -1 ? block.length : commentEnd + 2;
|
|
3504
|
+
output += block.slice(index, nextIndex);
|
|
3505
|
+
index = nextIndex;
|
|
3506
|
+
continue;
|
|
3507
|
+
}
|
|
3508
|
+
if (/\\s/.test(block[index] ?? "")) {
|
|
3509
|
+
output += block[index];
|
|
3510
|
+
index += 1;
|
|
3511
|
+
continue;
|
|
3512
|
+
}
|
|
3513
|
+
const keyframeRule = readRulePrelude(block, index, block.length);
|
|
3514
|
+
if (keyframeRule.blockStart === -1) {
|
|
3515
|
+
output += block.slice(index, keyframeRule.end);
|
|
3516
|
+
index = keyframeRule.end;
|
|
3517
|
+
continue;
|
|
3518
|
+
}
|
|
3519
|
+
const keyframeBlockEnd = findMatchingBrace(block, keyframeRule.blockStart, block.length);
|
|
3520
|
+
if (keyframeBlockEnd === -1) {
|
|
3521
|
+
output += block.slice(index);
|
|
3522
|
+
index = block.length;
|
|
3523
|
+
continue;
|
|
3524
|
+
}
|
|
3525
|
+
output += keyframeRule.prelude + "{" + namespaceStyleBlock(block.slice(keyframeRule.blockStart + 1, keyframeBlockEnd), keyframeNames, fontFamilyNames, customPropertyNames, assetName) + "}";
|
|
3526
|
+
index = keyframeBlockEnd + 1;
|
|
3527
|
+
}
|
|
3528
|
+
return output;
|
|
3529
|
+
}
|
|
3530
|
+
|
|
3531
|
+
function namespaceStyleBlock(block, keyframeNames, fontFamilyNames, customPropertyNames, assetName) {
|
|
3532
|
+
assertNoNestedCssBlock(block, assetName);
|
|
3533
|
+
return rewriteDeclarationBlock(block, (propertyName, rawProperty, value) => {
|
|
3534
|
+
const nextProperty = namespaceCustomPropertyName(rawProperty, propertyName, customPropertyNames);
|
|
3535
|
+
let nextValue = value;
|
|
3536
|
+
if (propertyName === "animation" || propertyName === "-webkit-animation") {
|
|
3537
|
+
nextValue = namespaceAnimationShorthandValue(nextValue, keyframeNames);
|
|
3538
|
+
} else if (propertyName === "animation-name" || propertyName === "-webkit-animation-name") {
|
|
3539
|
+
nextValue = namespaceAnimationNameValue(nextValue, keyframeNames);
|
|
3540
|
+
} else if (propertyName === "font-family") {
|
|
3541
|
+
nextValue = namespaceFontFamilyValue(nextValue, fontFamilyNames);
|
|
3542
|
+
} else if (propertyName === "font") {
|
|
3543
|
+
nextValue = namespaceFontShorthandValue(nextValue, fontFamilyNames, assetName);
|
|
3544
|
+
}
|
|
3545
|
+
if (propertyName === "container-name") {
|
|
3546
|
+
nextValue = namespaceContainerNameValue(nextValue);
|
|
3547
|
+
} else if (propertyName === "container") {
|
|
3548
|
+
nextValue = namespaceContainerShorthandValue(nextValue);
|
|
3549
|
+
}
|
|
3550
|
+
if (propertyName === "transition" || propertyName === "transition-property" || propertyName === "will-change") {
|
|
3551
|
+
nextValue = namespaceCustomPropertyIdentifierValue(nextValue, customPropertyNames);
|
|
3552
|
+
}
|
|
3553
|
+
if (propertyName === "view-transition-name" || propertyName === "scroll-timeline-name" || propertyName === "animation-timeline") {
|
|
3554
|
+
nextValue = namespaceCssGlobalNameValue(nextValue);
|
|
3555
|
+
}
|
|
3556
|
+
nextValue = namespaceCustomPropertyReferencesValue(nextValue, customPropertyNames);
|
|
3557
|
+
return { property: nextProperty, value: nextValue };
|
|
3558
|
+
});
|
|
3559
|
+
}
|
|
3560
|
+
|
|
3561
|
+
function namespaceFontFaceBlock(block, fontFamilyNames, customPropertyNames) {
|
|
3562
|
+
return rewriteDeclarationBlock(block, (propertyName, rawProperty, value) => {
|
|
3563
|
+
const nextValue = propertyName === "font-family"
|
|
3564
|
+
? namespaceFontFamilyValue(value, fontFamilyNames)
|
|
3565
|
+
: namespaceCustomPropertyReferencesValue(value, customPropertyNames);
|
|
3566
|
+
return { property: rawProperty, value: nextValue };
|
|
3567
|
+
});
|
|
3568
|
+
}
|
|
3569
|
+
|
|
3570
|
+
function namespaceAnimationNameValue(value, keyframeNames) {
|
|
3571
|
+
return replaceCssValueOutsideProtectedRanges(value, (chunk) => {
|
|
3572
|
+
let nextChunk = chunk;
|
|
3573
|
+
for (const [name, scopedName] of keyframeNames) {
|
|
3574
|
+
nextChunk = nextChunk.replace(new RegExp("(^|[^A-Za-z0-9_-])" + escapeRegExp(name) + "(?=$|[^A-Za-z0-9_-])", "g"), (_match, prefix) => prefix + scopedName);
|
|
3575
|
+
}
|
|
3576
|
+
return nextChunk;
|
|
3577
|
+
});
|
|
3578
|
+
}
|
|
3579
|
+
|
|
3580
|
+
function namespaceAnimationShorthandValue(value, keyframeNames) {
|
|
3581
|
+
if (keyframeNames.size === 0) return value;
|
|
3582
|
+
return splitCssCommaList(value).map((item) => rewriteAnimationShorthandItem(item, keyframeNames)).join(",");
|
|
3583
|
+
}
|
|
3584
|
+
|
|
3585
|
+
function rewriteAnimationShorthandItem(item, keyframeNames) {
|
|
3586
|
+
const tokens = readCssValueTokens(item);
|
|
3587
|
+
const reservedState = createAnimationShorthandReservedState();
|
|
3588
|
+
let output = "";
|
|
3589
|
+
let lastIndex = 0;
|
|
3590
|
+
for (const token of tokens) {
|
|
3591
|
+
const scopedName = keyframeNames.get(token.text);
|
|
3592
|
+
const isReservedToken = consumeAnimationShorthandReservedToken(token.text, reservedState);
|
|
3593
|
+
if (!scopedName || isReservedToken) continue;
|
|
3594
|
+
output += item.slice(lastIndex, token.start) + scopedName;
|
|
3595
|
+
lastIndex = token.end;
|
|
3596
|
+
}
|
|
3597
|
+
return output + item.slice(lastIndex);
|
|
3598
|
+
}
|
|
3599
|
+
|
|
3600
|
+
function createAnimationShorthandReservedState() {
|
|
3601
|
+
return {
|
|
3602
|
+
duration: false,
|
|
3603
|
+
delay: false,
|
|
3604
|
+
timingFunction: false,
|
|
3605
|
+
iterationCount: false,
|
|
3606
|
+
direction: false,
|
|
3607
|
+
fillMode: false,
|
|
3608
|
+
playState: false,
|
|
3609
|
+
};
|
|
3610
|
+
}
|
|
3611
|
+
|
|
3612
|
+
function consumeAnimationShorthandReservedToken(token, state) {
|
|
3613
|
+
const lower = token.toLowerCase();
|
|
3614
|
+
if (isAnimationTimeToken(token)) {
|
|
3615
|
+
if (!state.duration) {
|
|
3616
|
+
state.duration = true;
|
|
3617
|
+
return true;
|
|
3618
|
+
}
|
|
3619
|
+
if (!state.delay) {
|
|
3620
|
+
state.delay = true;
|
|
3621
|
+
return true;
|
|
3622
|
+
}
|
|
3623
|
+
return false;
|
|
3624
|
+
}
|
|
3625
|
+
if (!state.timingFunction && isAnimationTimingFunctionToken(lower)) {
|
|
3626
|
+
state.timingFunction = true;
|
|
3627
|
+
return true;
|
|
3628
|
+
}
|
|
3629
|
+
if (!state.iterationCount && isAnimationIterationCountToken(token, lower)) {
|
|
3630
|
+
state.iterationCount = true;
|
|
3631
|
+
return true;
|
|
3632
|
+
}
|
|
3633
|
+
if (!state.direction && animationDirectionKeywords.has(lower)) {
|
|
3634
|
+
state.direction = true;
|
|
3635
|
+
return true;
|
|
3636
|
+
}
|
|
3637
|
+
if (!state.fillMode && animationFillModeKeywords.has(lower)) {
|
|
3638
|
+
state.fillMode = true;
|
|
3639
|
+
return true;
|
|
3640
|
+
}
|
|
3641
|
+
if (!state.playState && animationPlayStateKeywords.has(lower)) {
|
|
3642
|
+
state.playState = true;
|
|
3643
|
+
return true;
|
|
3644
|
+
}
|
|
3645
|
+
if (animationGlobalKeywords.has(lower)) return true;
|
|
3646
|
+
return false;
|
|
3647
|
+
}
|
|
3648
|
+
|
|
3649
|
+
function isAnimationTimeToken(token) {
|
|
3650
|
+
return /^-?(?:\\d+|\\d*\\.\\d+)(?:ms|s)$/i.test(token);
|
|
3651
|
+
}
|
|
3652
|
+
|
|
3653
|
+
function isAnimationTimingFunctionToken(lowerToken) {
|
|
3654
|
+
return animationTimingFunctionKeywords.has(lowerToken) || lowerToken.includes("(");
|
|
3655
|
+
}
|
|
3656
|
+
|
|
3657
|
+
function isAnimationIterationCountToken(token, lowerToken) {
|
|
3658
|
+
return lowerToken === "infinite" || /^-?(?:\\d+|\\d*\\.\\d+)$/.test(token);
|
|
3659
|
+
}
|
|
3660
|
+
|
|
3661
|
+
const animationTimingFunctionKeywords = new Set([
|
|
3662
|
+
"ease",
|
|
3663
|
+
"ease-in",
|
|
3664
|
+
"ease-in-out",
|
|
3665
|
+
"ease-out",
|
|
3666
|
+
"linear",
|
|
3667
|
+
"step-end",
|
|
3668
|
+
"step-start",
|
|
3669
|
+
]);
|
|
3670
|
+
const animationDirectionKeywords = new Set(["normal", "reverse", "alternate", "alternate-reverse"]);
|
|
3671
|
+
const animationFillModeKeywords = new Set(["none", "forwards", "backwards", "both"]);
|
|
3672
|
+
const animationPlayStateKeywords = new Set(["running", "paused"]);
|
|
3673
|
+
const animationGlobalKeywords = new Set(["inherit", "initial", "revert", "revert-layer", "unset"]);
|
|
3674
|
+
|
|
3675
|
+
function namespaceFontFamilyValue(value, fontFamilyNames) {
|
|
3676
|
+
if (fontFamilyNames.size === 0) return value;
|
|
3677
|
+
const important = splitCssImportantSuffix(value);
|
|
3678
|
+
return splitCssCommaList(important.value).map((family) => namespaceSingleFontFamily(family, fontFamilyNames)).join(",") + important.suffix;
|
|
3679
|
+
}
|
|
3680
|
+
|
|
3681
|
+
function namespaceFontShorthandValue(value, fontFamilyNames, assetName) {
|
|
3682
|
+
if (fontFamilyNames.size === 0) return value;
|
|
3683
|
+
const familyStart = findFontShorthandFamilyStart(value);
|
|
3684
|
+
if (familyStart === -1) {
|
|
3685
|
+
if (fontShorthandReferencesScopedFontFamily(value, fontFamilyNames)) {
|
|
3686
|
+
throw new Error('Unsupported font shorthand in CSS asset "' + assetName + '" cannot be isolated safely. Use font-family with font-style/font-weight/font-size longhands, or use a supported font shorthand with an explicit font-size before the family list.');
|
|
3687
|
+
}
|
|
3688
|
+
return value;
|
|
3689
|
+
}
|
|
3690
|
+
return value.slice(0, familyStart) + namespaceFontFamilyValue(value.slice(familyStart), fontFamilyNames);
|
|
3691
|
+
}
|
|
3692
|
+
|
|
3693
|
+
function fontShorthandReferencesScopedFontFamily(value, fontFamilyNames) {
|
|
3694
|
+
const tokens = readCssValueTokens(value);
|
|
3695
|
+
return tokens.some((token) => fontFamilyNames.has(normalizeFontFamilyName(token.text)));
|
|
3696
|
+
}
|
|
3697
|
+
|
|
3698
|
+
function splitCssImportantSuffix(value) {
|
|
3699
|
+
const match = /!\\s*important\\s*$/i.exec(value);
|
|
3700
|
+
if (!match) return { value, suffix: "" };
|
|
3701
|
+
return {
|
|
3702
|
+
value: value.slice(0, match.index),
|
|
3703
|
+
suffix: value.slice(match.index),
|
|
3704
|
+
};
|
|
3705
|
+
}
|
|
3706
|
+
|
|
3707
|
+
function namespaceSingleFontFamily(family, fontFamilyNames) {
|
|
3708
|
+
const leadingWhitespace = family.match(/^\\s*/)?.[0] ?? "";
|
|
3709
|
+
const trailingWhitespace = family.match(/\\s*$/)?.[0] ?? "";
|
|
3710
|
+
const trimmed = family.trim();
|
|
3711
|
+
const normalized = normalizeFontFamilyName(trimmed);
|
|
3712
|
+
const scopedName = fontFamilyNames.get(normalized);
|
|
3713
|
+
if (!scopedName) return family;
|
|
3714
|
+
const quote = trimmed[0] === '"' || trimmed[0] === "'" ? trimmed[0] : "";
|
|
3715
|
+
return leadingWhitespace + (quote ? quote + scopedName + quote : scopedName) + trailingWhitespace;
|
|
3716
|
+
}
|
|
3717
|
+
|
|
3718
|
+
function normalizeFontFamilyName(value) {
|
|
3719
|
+
const trimmed = value.trim();
|
|
3720
|
+
if ((trimmed.startsWith('"') && trimmed.endsWith('"')) || (trimmed.startsWith("'") && trimmed.endsWith("'"))) {
|
|
3721
|
+
return trimmed.slice(1, -1);
|
|
3722
|
+
}
|
|
3723
|
+
return trimmed;
|
|
3724
|
+
}
|
|
3725
|
+
|
|
3726
|
+
function rewritePropertyHeader(header, customPropertyNames) {
|
|
3727
|
+
let nextHeader = header;
|
|
3728
|
+
for (const [name, scopedName] of customPropertyNames) {
|
|
3729
|
+
nextHeader = nextHeader.replace(new RegExp("(@property\\\\s+)" + escapeRegExp(name) + "(?=$|[\\\\s{])", "g"), (_match, prefix) => prefix + scopedName);
|
|
3730
|
+
}
|
|
3731
|
+
return nextHeader;
|
|
3732
|
+
}
|
|
3733
|
+
|
|
3734
|
+
function rewriteLayerPrelude(prelude) {
|
|
3735
|
+
const match = /^(@layer\\b)([\\s\\S]*)$/i.exec(prelude);
|
|
3736
|
+
if (!match) return prelude;
|
|
3737
|
+
const layerList = match[2] ?? "";
|
|
3738
|
+
if (!layerList.trim()) return prelude;
|
|
3739
|
+
return match[1] + splitCssCommaList(layerList).map(namespaceLayerName).join(",");
|
|
3740
|
+
}
|
|
3741
|
+
|
|
3742
|
+
function namespaceLayerName(layerName) {
|
|
3743
|
+
const leadingWhitespace = layerName.match(/^\\s*/)?.[0] ?? "";
|
|
3744
|
+
const trailingWhitespace = layerName.match(/\\s*$/)?.[0] ?? "";
|
|
3745
|
+
const trimmed = layerName.trim();
|
|
3746
|
+
return trimmed ? leadingWhitespace + widgetPackageLayerPrefix + trimmed + trailingWhitespace : layerName;
|
|
3747
|
+
}
|
|
3748
|
+
|
|
3749
|
+
function rewriteContainerAtRuleHeader(header) {
|
|
3750
|
+
if (!header.endsWith("{")) return header;
|
|
3751
|
+
return rewriteContainerAtRulePrelude(header.slice(0, -1)) + "{";
|
|
3752
|
+
}
|
|
3753
|
+
|
|
3754
|
+
function rewriteContainerAtRulePrelude(prelude) {
|
|
3755
|
+
const match = /^(@container\\b)([\\s\\S]*)$/i.exec(prelude);
|
|
3756
|
+
if (!match) return prelude;
|
|
3757
|
+
const condition = match[2] ?? "";
|
|
3758
|
+
const leadingWhitespace = condition.match(/^\\s*/)?.[0] ?? "";
|
|
3759
|
+
const conditionBody = condition.slice(leadingWhitespace.length);
|
|
3760
|
+
if (!conditionBody || conditionBody.startsWith("(") || /^(?:style|scroll-state)\\s*\\(/.test(conditionBody)) {
|
|
3761
|
+
return prelude;
|
|
3762
|
+
}
|
|
3763
|
+
|
|
3764
|
+
const tokens = readCssValueTokens(conditionBody);
|
|
3765
|
+
const nameToken = tokens[0];
|
|
3766
|
+
if (!nameToken || nameToken.start !== 0 || isContainerQueryConditionKeyword(nameToken.text) || isReservedContainerName(nameToken.text) || nameToken.text.includes("(")) {
|
|
3767
|
+
return prelude;
|
|
3768
|
+
}
|
|
3769
|
+
return match[1] + leadingWhitespace + namespaceContainerName(nameToken.text) + conditionBody.slice(nameToken.end);
|
|
3770
|
+
}
|
|
3771
|
+
|
|
3772
|
+
function namespaceContainerNameValue(value) {
|
|
3773
|
+
return replaceCssValueOutsideProtectedRanges(value, (chunk) => namespaceContainerNamesInChunk(chunk));
|
|
3774
|
+
}
|
|
3775
|
+
|
|
3776
|
+
function namespaceContainerShorthandValue(value) {
|
|
3777
|
+
const slashIndex = findTopLevelSlash(value);
|
|
3778
|
+
if (slashIndex !== -1) {
|
|
3779
|
+
return namespaceContainerNameValue(value.slice(0, slashIndex)) + value.slice(slashIndex);
|
|
3780
|
+
}
|
|
3781
|
+
|
|
3782
|
+
const trimmed = stripCssComments(value).trim().toLowerCase();
|
|
3783
|
+
if (isReservedContainerName(trimmed) || containerTypeKeywords.has(trimmed)) return value;
|
|
3784
|
+
return namespaceContainerNameValue(value);
|
|
3785
|
+
}
|
|
3786
|
+
|
|
3787
|
+
function namespaceContainerNamesInChunk(chunk) {
|
|
3788
|
+
return chunk.replace(/(^|[^A-Za-z0-9_-])([A-Za-z_][A-Za-z0-9_-]*|-[A-Za-z_][A-Za-z0-9_-]*)(?=$|[^A-Za-z0-9_-])/g, (_match, prefix, name) => {
|
|
3789
|
+
if (isReservedContainerName(name)) return prefix + name;
|
|
3790
|
+
return prefix + namespaceContainerName(name);
|
|
3791
|
+
});
|
|
3792
|
+
}
|
|
3793
|
+
|
|
3794
|
+
function namespaceContainerName(name) {
|
|
3795
|
+
return widgetPackageContainerPrefix + name;
|
|
3796
|
+
}
|
|
3797
|
+
|
|
3798
|
+
function isReservedContainerName(name) {
|
|
3799
|
+
return containerNameReservedKeywords.has(name.toLowerCase());
|
|
3800
|
+
}
|
|
3801
|
+
|
|
3802
|
+
function isContainerQueryConditionKeyword(name) {
|
|
3803
|
+
return containerQueryConditionKeywords.has(name.toLowerCase());
|
|
3804
|
+
}
|
|
3805
|
+
|
|
3806
|
+
const containerTypeKeywords = new Set(["normal", "size", "inline-size", "scroll-state"]);
|
|
3807
|
+
const containerQueryConditionKeywords = new Set(["not", "and", "or"]);
|
|
3808
|
+
const containerNameReservedKeywords = new Set(["none", "inherit", "initial", "revert", "revert-layer", "unset"]);
|
|
3809
|
+
|
|
3810
|
+
function namespaceCustomPropertyName(rawProperty, propertyName, customPropertyNames) {
|
|
3811
|
+
const scopedName = customPropertyNames.get(propertyName);
|
|
3812
|
+
if (!scopedName) return rawProperty;
|
|
3813
|
+
return rawProperty.replace(new RegExp(escapeRegExp(propertyName) + "(?=\\\\s*$)"), scopedName);
|
|
3814
|
+
}
|
|
3815
|
+
|
|
3816
|
+
function namespaceCustomPropertyReferences(block, customPropertyNames) {
|
|
3817
|
+
return rewriteDeclarationBlock(block, (propertyName, rawProperty, value) => ({
|
|
3818
|
+
property: rawProperty,
|
|
3819
|
+
value: namespaceCustomPropertyReferencesValue(value, customPropertyNames),
|
|
3820
|
+
}));
|
|
3821
|
+
}
|
|
3822
|
+
|
|
3823
|
+
function namespaceCustomPropertyReferencesValue(value, customPropertyNames) {
|
|
3824
|
+
if (customPropertyNames.size === 0) return value;
|
|
3825
|
+
return replaceCssValueOutsideProtectedRanges(value, (chunk) => {
|
|
3826
|
+
let nextChunk = chunk;
|
|
3827
|
+
for (const [name, scopedName] of customPropertyNames) {
|
|
3828
|
+
nextChunk = nextChunk.replace(new RegExp("(\\\\bvar\\\\(\\\\s*)" + escapeRegExp(name) + "(?=\\\\s*[,)])", "g"), (_match, prefix) => prefix + scopedName);
|
|
3829
|
+
}
|
|
3830
|
+
return nextChunk;
|
|
3831
|
+
});
|
|
3832
|
+
}
|
|
3833
|
+
|
|
3834
|
+
function namespaceCustomPropertyIdentifierValue(value, customPropertyNames) {
|
|
3835
|
+
if (customPropertyNames.size === 0) return value;
|
|
3836
|
+
return replaceCssValueOutsideProtectedRanges(value, (chunk) => {
|
|
3837
|
+
let nextChunk = chunk;
|
|
3838
|
+
for (const [name, scopedName] of customPropertyNames) {
|
|
3839
|
+
nextChunk = nextChunk.replace(new RegExp("(^|[^A-Za-z0-9_-])" + escapeRegExp(name) + "(?=$|[^A-Za-z0-9_-])", "g"), (_match, prefix) => prefix + scopedName);
|
|
3840
|
+
}
|
|
3841
|
+
return nextChunk;
|
|
3842
|
+
});
|
|
3843
|
+
}
|
|
3844
|
+
|
|
3845
|
+
function namespaceCssGlobalNameValue(value) {
|
|
3846
|
+
return replaceCssValueOutsideProtectedRanges(value, (chunk) => {
|
|
3847
|
+
return chunk.replace(/(^|[^A-Za-z0-9_-])(--[A-Za-z0-9_-]+|[A-Za-z_][A-Za-z0-9_-]*|-[A-Za-z_][A-Za-z0-9_-]*)(?=$|[^A-Za-z0-9_-])/g, (_match, prefix, name, offset, source) => {
|
|
3848
|
+
const nextChar = source[offset + prefix.length + name.length] ?? "";
|
|
3849
|
+
const beforeName = source.slice(0, offset + prefix.length);
|
|
3850
|
+
if (nextChar === "(" || /(?:^|[^A-Za-z0-9_-])var\\s*\\($/i.test(beforeName) || cssGlobalNameReservedKeywords.has(name.toLowerCase())) return prefix + name;
|
|
3851
|
+
return prefix + widgetPackageCssGlobalNamePrefix + name;
|
|
3852
|
+
});
|
|
3853
|
+
});
|
|
3854
|
+
}
|
|
3855
|
+
|
|
3856
|
+
const cssGlobalNameReservedKeywords = new Set(["auto", "none", "inherit", "initial", "revert", "revert-layer", "unset"]);
|
|
3857
|
+
|
|
3858
|
+
function assertNoNestedCssBlock(block, assetName) {
|
|
3859
|
+
let index = 0;
|
|
3860
|
+
let quote = "";
|
|
3861
|
+
let parenDepth = 0;
|
|
3862
|
+
while (index < block.length) {
|
|
3863
|
+
const char = block[index];
|
|
3864
|
+
if (quote) {
|
|
3865
|
+
if (char === "\\\\") index += 2;
|
|
3866
|
+
else {
|
|
3867
|
+
if (char === quote) quote = "";
|
|
3868
|
+
index += 1;
|
|
3869
|
+
}
|
|
3870
|
+
continue;
|
|
3871
|
+
}
|
|
3872
|
+
if (cssStartsWithComment(block, index)) {
|
|
3873
|
+
const commentEnd = block.indexOf("*/", index + 2);
|
|
3874
|
+
index = commentEnd === -1 ? block.length : commentEnd + 2;
|
|
3875
|
+
continue;
|
|
3876
|
+
}
|
|
3877
|
+
if (char === '"' || char === "'") {
|
|
3878
|
+
quote = char;
|
|
3879
|
+
index += 1;
|
|
3880
|
+
continue;
|
|
3881
|
+
}
|
|
3882
|
+
if (char === "(") parenDepth += 1;
|
|
3883
|
+
else if (char === ")") parenDepth = Math.max(0, parenDepth - 1);
|
|
3884
|
+
else if (char === "{" && parenDepth === 0) {
|
|
3885
|
+
throw new Error('Unsupported nested CSS rule in CSS asset "' + assetName + '" cannot be isolated safely. Compile nested CSS before building the widget package.');
|
|
3886
|
+
}
|
|
3887
|
+
index += 1;
|
|
3888
|
+
}
|
|
3889
|
+
}
|
|
3890
|
+
|
|
3891
|
+
function rewriteDeclarationBlock(block, rewriteDeclaration) {
|
|
3892
|
+
let output = "";
|
|
3893
|
+
let declarationStart = 0;
|
|
3894
|
+
let index = 0;
|
|
3895
|
+
let quote = "";
|
|
3896
|
+
let parenDepth = 0;
|
|
3897
|
+
while (index < block.length) {
|
|
3898
|
+
const char = block[index];
|
|
3899
|
+
if (quote) {
|
|
3900
|
+
if (char === "\\\\") index += 2;
|
|
3901
|
+
else {
|
|
3902
|
+
if (char === quote) quote = "";
|
|
3903
|
+
index += 1;
|
|
3904
|
+
}
|
|
3905
|
+
continue;
|
|
3906
|
+
}
|
|
3907
|
+
if (cssStartsWithComment(block, index)) {
|
|
3908
|
+
const commentEnd = block.indexOf("*/", index + 2);
|
|
3909
|
+
index = commentEnd === -1 ? block.length : commentEnd + 2;
|
|
3910
|
+
continue;
|
|
3911
|
+
}
|
|
3912
|
+
if (char === '"' || char === "'") {
|
|
3913
|
+
quote = char;
|
|
3914
|
+
index += 1;
|
|
3915
|
+
continue;
|
|
3916
|
+
}
|
|
3917
|
+
if (char === "(") parenDepth += 1;
|
|
3918
|
+
else if (char === ")") parenDepth = Math.max(0, parenDepth - 1);
|
|
3919
|
+
else if (char === ";" && parenDepth === 0) {
|
|
3920
|
+
output += rewriteDeclarationSegment(block.slice(declarationStart, index), rewriteDeclaration) + ";";
|
|
3921
|
+
declarationStart = index + 1;
|
|
3922
|
+
}
|
|
3923
|
+
index += 1;
|
|
3924
|
+
}
|
|
3925
|
+
return output + rewriteDeclarationSegment(block.slice(declarationStart), rewriteDeclaration);
|
|
3926
|
+
}
|
|
3927
|
+
|
|
3928
|
+
function rewriteDeclarationSegment(segment, rewriteDeclaration) {
|
|
3929
|
+
const colonIndex = findDeclarationColon(segment);
|
|
3930
|
+
if (colonIndex === -1) return segment;
|
|
3931
|
+
const rawProperty = segment.slice(0, colonIndex);
|
|
3932
|
+
const rawPropertyName = stripCssComments(rawProperty).trim();
|
|
3933
|
+
const propertyName = rawPropertyName.startsWith("--") ? rawPropertyName : rawPropertyName.toLowerCase();
|
|
3934
|
+
if (!propertyName) return segment;
|
|
3935
|
+
const rewritten = rewriteDeclaration(propertyName, rawProperty, segment.slice(colonIndex + 1));
|
|
3936
|
+
return rewritten.property + ":" + rewritten.value;
|
|
3937
|
+
}
|
|
3938
|
+
|
|
3939
|
+
function findDeclarationColon(segment) {
|
|
3940
|
+
let index = 0;
|
|
3941
|
+
let quote = "";
|
|
3942
|
+
let parenDepth = 0;
|
|
3943
|
+
while (index < segment.length) {
|
|
3944
|
+
const char = segment[index];
|
|
3945
|
+
if (quote) {
|
|
3946
|
+
if (char === "\\\\") index += 2;
|
|
3947
|
+
else {
|
|
3948
|
+
if (char === quote) quote = "";
|
|
3949
|
+
index += 1;
|
|
3950
|
+
}
|
|
3951
|
+
continue;
|
|
3952
|
+
}
|
|
3953
|
+
if (cssStartsWithComment(segment, index)) {
|
|
3954
|
+
const commentEnd = segment.indexOf("*/", index + 2);
|
|
3955
|
+
index = commentEnd === -1 ? segment.length : commentEnd + 2;
|
|
3956
|
+
continue;
|
|
3957
|
+
}
|
|
3958
|
+
if (char === '"' || char === "'") {
|
|
3959
|
+
quote = char;
|
|
3960
|
+
index += 1;
|
|
3961
|
+
continue;
|
|
3962
|
+
}
|
|
3963
|
+
if (char === "(") parenDepth += 1;
|
|
3964
|
+
else if (char === ")") parenDepth = Math.max(0, parenDepth - 1);
|
|
3965
|
+
else if (char === ":" && parenDepth === 0) return index;
|
|
3966
|
+
index += 1;
|
|
3967
|
+
}
|
|
3968
|
+
return -1;
|
|
3969
|
+
}
|
|
3970
|
+
|
|
3971
|
+
function replaceCssValueOutsideProtectedRanges(value, replaceChunk) {
|
|
3972
|
+
let output = "";
|
|
3973
|
+
let chunkStart = 0;
|
|
3974
|
+
let index = 0;
|
|
3975
|
+
while (index < value.length) {
|
|
3976
|
+
if (cssStartsWithComment(value, index)) {
|
|
3977
|
+
output += replaceChunk(value.slice(chunkStart, index));
|
|
3978
|
+
const commentEnd = value.indexOf("*/", index + 2);
|
|
3979
|
+
const nextIndex = commentEnd === -1 ? value.length : commentEnd + 2;
|
|
3980
|
+
output += value.slice(index, nextIndex);
|
|
3981
|
+
index = nextIndex;
|
|
3982
|
+
chunkStart = index;
|
|
3983
|
+
continue;
|
|
3984
|
+
}
|
|
3985
|
+
const char = value[index];
|
|
3986
|
+
if (char === '"' || char === "'") {
|
|
3987
|
+
output += replaceChunk(value.slice(chunkStart, index));
|
|
3988
|
+
const stringEnd = findCssStringEnd(value, index);
|
|
3989
|
+
output += value.slice(index, stringEnd);
|
|
3990
|
+
index = stringEnd;
|
|
3991
|
+
chunkStart = index;
|
|
3992
|
+
continue;
|
|
3993
|
+
}
|
|
3994
|
+
if (isCssUrlFunctionStart(value, index)) {
|
|
3995
|
+
output += replaceChunk(value.slice(chunkStart, index));
|
|
3996
|
+
const urlEnd = findCssFunctionEnd(value, value.indexOf("(", index));
|
|
3997
|
+
output += value.slice(index, urlEnd);
|
|
3998
|
+
index = urlEnd;
|
|
3999
|
+
chunkStart = index;
|
|
4000
|
+
continue;
|
|
4001
|
+
}
|
|
4002
|
+
index += 1;
|
|
4003
|
+
}
|
|
4004
|
+
return output + replaceChunk(value.slice(chunkStart));
|
|
4005
|
+
}
|
|
4006
|
+
|
|
4007
|
+
function findCssStringEnd(value, quoteIndex) {
|
|
4008
|
+
const quote = value[quoteIndex];
|
|
4009
|
+
let index = quoteIndex + 1;
|
|
4010
|
+
while (index < value.length) {
|
|
4011
|
+
const char = value[index];
|
|
4012
|
+
if (char === "\\\\") index += 2;
|
|
4013
|
+
else {
|
|
4014
|
+
index += 1;
|
|
4015
|
+
if (char === quote) return index;
|
|
4016
|
+
}
|
|
4017
|
+
}
|
|
4018
|
+
return value.length;
|
|
4019
|
+
}
|
|
4020
|
+
|
|
4021
|
+
function findCssFunctionEnd(value, openParenIndex) {
|
|
4022
|
+
let index = openParenIndex + 1;
|
|
4023
|
+
let depth = 1;
|
|
4024
|
+
let quote = "";
|
|
4025
|
+
while (index < value.length) {
|
|
4026
|
+
const char = value[index];
|
|
4027
|
+
if (quote) {
|
|
4028
|
+
if (char === "\\\\") index += 2;
|
|
4029
|
+
else {
|
|
4030
|
+
if (char === quote) quote = "";
|
|
4031
|
+
index += 1;
|
|
4032
|
+
}
|
|
4033
|
+
continue;
|
|
4034
|
+
}
|
|
4035
|
+
if (char === '"' || char === "'") {
|
|
4036
|
+
quote = char;
|
|
4037
|
+
index += 1;
|
|
4038
|
+
continue;
|
|
4039
|
+
}
|
|
4040
|
+
if (cssStartsWithComment(value, index)) {
|
|
4041
|
+
const commentEnd = value.indexOf("*/", index + 2);
|
|
4042
|
+
index = commentEnd === -1 ? value.length : commentEnd + 2;
|
|
4043
|
+
continue;
|
|
4044
|
+
}
|
|
4045
|
+
if (char === "(") depth += 1;
|
|
4046
|
+
else if (char === ")") {
|
|
4047
|
+
depth -= 1;
|
|
4048
|
+
if (depth === 0) return index + 1;
|
|
4049
|
+
}
|
|
4050
|
+
index += 1;
|
|
4051
|
+
}
|
|
4052
|
+
return value.length;
|
|
4053
|
+
}
|
|
4054
|
+
|
|
4055
|
+
function findFontShorthandFamilyStart(value) {
|
|
4056
|
+
const tokens = readCssValueTokens(value);
|
|
4057
|
+
for (let index = 0; index < tokens.length; index += 1) {
|
|
4058
|
+
const token = tokens[index];
|
|
4059
|
+
if (!token || !isFontSizeToken(token.text)) continue;
|
|
4060
|
+
if (findTopLevelSlash(token.text) !== -1) return token.end;
|
|
4061
|
+
const slashIndex = skipCssWhitespaceAndComments(value, token.end);
|
|
4062
|
+
if (value[slashIndex] !== "/") return token.end;
|
|
4063
|
+
const lineHeightStart = skipCssWhitespaceAndComments(value, slashIndex + 1);
|
|
4064
|
+
const lineHeightToken = tokens.find((candidate) => candidate.start >= lineHeightStart);
|
|
4065
|
+
return lineHeightToken ? lineHeightToken.end : token.end;
|
|
4066
|
+
}
|
|
4067
|
+
return -1;
|
|
4068
|
+
}
|
|
4069
|
+
|
|
4070
|
+
function readCssValueTokens(value) {
|
|
4071
|
+
const tokens = [];
|
|
4072
|
+
let index = 0;
|
|
4073
|
+
while (index < value.length) {
|
|
4074
|
+
if (/\\s/.test(value[index] ?? "")) {
|
|
4075
|
+
index += 1;
|
|
4076
|
+
continue;
|
|
4077
|
+
}
|
|
4078
|
+
if (cssStartsWithComment(value, index)) {
|
|
4079
|
+
const commentEnd = value.indexOf("*/", index + 2);
|
|
4080
|
+
index = commentEnd === -1 ? value.length : commentEnd + 2;
|
|
4081
|
+
continue;
|
|
4082
|
+
}
|
|
4083
|
+
const start = index;
|
|
4084
|
+
const char = value[index];
|
|
4085
|
+
if (char === '"' || char === "'") {
|
|
4086
|
+
index = findCssStringEnd(value, index);
|
|
4087
|
+
} else {
|
|
4088
|
+
let parenDepth = 0;
|
|
4089
|
+
while (index < value.length) {
|
|
4090
|
+
if (cssStartsWithComment(value, index)) {
|
|
4091
|
+
if (parenDepth === 0) break;
|
|
4092
|
+
const commentEnd = value.indexOf("*/", index + 2);
|
|
4093
|
+
index = commentEnd === -1 ? value.length : commentEnd + 2;
|
|
4094
|
+
continue;
|
|
4095
|
+
}
|
|
4096
|
+
const tokenChar = value[index];
|
|
4097
|
+
if ((tokenChar === '"' || tokenChar === "'") && parenDepth > 0) {
|
|
4098
|
+
index = findCssStringEnd(value, index);
|
|
4099
|
+
continue;
|
|
4100
|
+
}
|
|
4101
|
+
if (tokenChar === "(") parenDepth += 1;
|
|
4102
|
+
else if (tokenChar === ")") parenDepth = Math.max(0, parenDepth - 1);
|
|
4103
|
+
if (parenDepth === 0 && /\\s/.test(tokenChar ?? "")) break;
|
|
4104
|
+
index += 1;
|
|
4105
|
+
}
|
|
4106
|
+
}
|
|
4107
|
+
tokens.push({ start, end: index, text: value.slice(start, index) });
|
|
4108
|
+
}
|
|
4109
|
+
return tokens;
|
|
4110
|
+
}
|
|
4111
|
+
|
|
4112
|
+
function isFontSizeToken(token) {
|
|
4113
|
+
const slashIndex = findTopLevelSlash(token);
|
|
4114
|
+
const sizeToken = slashIndex === -1 ? token : token.slice(0, slashIndex);
|
|
4115
|
+
return /^(?:xx-small|x-small|small|medium|large|x-large|xx-large|xxx-large|smaller|larger|math|(?:\\d+|\\d*\\.\\d+)(?:px|em|rem|%|vh|vw|vmin|vmax|ch|ex|cap|ic|lh|rlh|cm|mm|q|in|pc|pt))$/i.test(sizeToken) || isFontSizeFunctionToken(sizeToken);
|
|
4116
|
+
}
|
|
4117
|
+
|
|
4118
|
+
function isFontSizeFunctionToken(token) {
|
|
4119
|
+
return /^(?:calc|clamp|min|max|var|env|abs|sign|round|mod|rem|sin|cos|tan|asin|acos|atan|atan2|pow|sqrt|hypot|log|exp)\\(/i.test(token);
|
|
4120
|
+
}
|
|
4121
|
+
|
|
4122
|
+
function findTopLevelSlash(value) {
|
|
4123
|
+
let index = 0;
|
|
4124
|
+
let quote = "";
|
|
4125
|
+
let parenDepth = 0;
|
|
4126
|
+
while (index < value.length) {
|
|
4127
|
+
const char = value[index];
|
|
4128
|
+
if (quote) {
|
|
4129
|
+
if (char === "\\\\") index += 2;
|
|
4130
|
+
else {
|
|
4131
|
+
if (char === quote) quote = "";
|
|
4132
|
+
index += 1;
|
|
4133
|
+
}
|
|
4134
|
+
continue;
|
|
4135
|
+
}
|
|
4136
|
+
if (char === '"' || char === "'") {
|
|
4137
|
+
quote = char;
|
|
4138
|
+
index += 1;
|
|
4139
|
+
continue;
|
|
4140
|
+
}
|
|
4141
|
+
if (char === "(") parenDepth += 1;
|
|
4142
|
+
else if (char === ")") parenDepth = Math.max(0, parenDepth - 1);
|
|
4143
|
+
else if (char === "/" && parenDepth === 0) return index;
|
|
4144
|
+
index += 1;
|
|
4145
|
+
}
|
|
4146
|
+
return -1;
|
|
4147
|
+
}
|
|
4148
|
+
|
|
4149
|
+
function skipCssWhitespaceAndComments(value, startIndex) {
|
|
4150
|
+
let index = startIndex;
|
|
4151
|
+
while (index < value.length) {
|
|
4152
|
+
if (/\\s/.test(value[index] ?? "")) {
|
|
4153
|
+
index += 1;
|
|
4154
|
+
continue;
|
|
4155
|
+
}
|
|
4156
|
+
if (cssStartsWithComment(value, index)) {
|
|
4157
|
+
const commentEnd = value.indexOf("*/", index + 2);
|
|
4158
|
+
index = commentEnd === -1 ? value.length : commentEnd + 2;
|
|
4159
|
+
continue;
|
|
4160
|
+
}
|
|
4161
|
+
return index;
|
|
4162
|
+
}
|
|
4163
|
+
return index;
|
|
4164
|
+
}
|
|
4165
|
+
|
|
4166
|
+
function cssStartsWithComment(value, index) {
|
|
4167
|
+
return value[index] === "/" && value[index + 1] === "*";
|
|
4168
|
+
}
|
|
4169
|
+
|
|
4170
|
+
function isCssUrlFunctionStart(value, index) {
|
|
4171
|
+
return /^url\\s*\\(/i.test(value.slice(index)) && !/[A-Za-z0-9_-]/.test(value[index - 1] ?? "");
|
|
4172
|
+
}
|
|
4173
|
+
|
|
4174
|
+
function stripCssComments(value) {
|
|
4175
|
+
return value.replace(/\\/\\*[\\s\\S]*?\\*\\//g, "");
|
|
4176
|
+
}
|
|
4177
|
+
|
|
4178
|
+
function splitCssCommaList(value) {
|
|
4179
|
+
const parts = [];
|
|
4180
|
+
let current = "";
|
|
4181
|
+
let quote = "";
|
|
4182
|
+
let parenDepth = 0;
|
|
4183
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
4184
|
+
const char = value[index];
|
|
4185
|
+
if (quote) {
|
|
4186
|
+
current += char;
|
|
4187
|
+
if (char === "\\\\") {
|
|
4188
|
+
index += 1;
|
|
4189
|
+
current += value[index] ?? "";
|
|
4190
|
+
} else if (char === quote) quote = "";
|
|
4191
|
+
continue;
|
|
4192
|
+
}
|
|
4193
|
+
if (char === '"' || char === "'") {
|
|
4194
|
+
quote = char;
|
|
4195
|
+
current += char;
|
|
4196
|
+
continue;
|
|
4197
|
+
}
|
|
4198
|
+
if (char === "(") parenDepth += 1;
|
|
4199
|
+
else if (char === ")") parenDepth = Math.max(0, parenDepth - 1);
|
|
4200
|
+
if (char === "," && parenDepth === 0) {
|
|
4201
|
+
parts.push(current);
|
|
4202
|
+
current = "";
|
|
4203
|
+
} else current += char;
|
|
4204
|
+
}
|
|
4205
|
+
parts.push(current);
|
|
4206
|
+
return parts;
|
|
4207
|
+
}
|
|
4208
|
+
|
|
4209
|
+
function escapeRegExp(value) {
|
|
4210
|
+
return value.replace(/[.*+?^\${}()|[\\]\\\\]/g, "\\\\$&");
|
|
4211
|
+
}
|
|
4212
|
+
|
|
4213
|
+
function encodeCssIdentifier(value) {
|
|
4214
|
+
return Array.from(value, (char) => char.codePointAt(0)?.toString(16).padStart(2, "0") ?? "00").join("_");
|
|
4215
|
+
}
|
|
4216
|
+
|
|
2991
4217
|
return {
|
|
2992
4218
|
${configFileSource} publicDir: false,
|
|
2993
4219
|
resolve: {
|
|
2994
4220
|
conditions: ["fluid-widget-authoring"],
|
|
2995
4221
|
},
|
|
2996
|
-
plugins: [fluidWidgetBuildInvariantsPlugin()],
|
|
4222
|
+
plugins: [fluidWidgetBuildInvariantsPlugin(), fluidWidgetCssIsolationPlugin()],
|
|
2997
4223
|
build: createFluidWidgetBuildInvariants(),
|
|
2998
4224
|
};
|
|
2999
4225
|
})()`;
|