@osdk/widget.vite-plugin 3.2.3 → 3.2.5
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/CHANGELOG.md +21 -0
- package/build/esm/build-plugin/__tests__/validateWidgetSet.test.js +44 -0
- package/build/esm/build-plugin/__tests__/validateWidgetSet.test.js.map +1 -0
- package/build/esm/build-plugin/buildWidgetSetManifest.js +2 -0
- package/build/esm/build-plugin/buildWidgetSetManifest.js.map +1 -1
- package/build/esm/build-plugin/validateWidgetSet.js +30 -0
- package/build/esm/build-plugin/validateWidgetSet.js.map +1 -0
- package/build/esm/client/app.js +5 -3
- package/build/esm/client/app.js.map +1 -1
- package/build/esm/common/__tests__/extractWidgetConfig.test.js +3 -3
- package/build/esm/common/__tests__/extractWidgetConfig.test.js.map +1 -1
- package/build/esm/common/__tests__/standardizePathAndFileExtension.test.js +77 -0
- package/build/esm/common/__tests__/standardizePathAndFileExtension.test.js.map +1 -0
- package/build/esm/common/extractWidgetConfig.js +4 -1
- package/build/esm/common/extractWidgetConfig.js.map +1 -1
- package/build/esm/common/{standardizeFileExtension.js → standardizePathAndFileExtension.js} +13 -4
- package/build/esm/common/standardizePathAndFileExtension.js.map +1 -0
- package/build/esm/dev-plugin/FoundryWidgetDevPlugin.js +41 -11
- package/build/esm/dev-plugin/FoundryWidgetDevPlugin.js.map +1 -1
- package/build/esm/dev-plugin/__tests__/validateDevEnvironment.test.js +66 -0
- package/build/esm/dev-plugin/__tests__/validateDevEnvironment.test.js.map +1 -0
- package/build/esm/dev-plugin/validateDevEnvironment.js +33 -0
- package/build/esm/dev-plugin/validateDevEnvironment.js.map +1 -0
- package/build/site/assets/{allPaths-CuhL1v2w.js → allPaths-DAStXfot.js} +1 -1
- package/build/site/assets/{allPathsLoader-B099rzRl.js → allPathsLoader-Xesz75gG.js} +2 -2
- package/build/site/assets/{index-gnK42Oev.js → index-C5U_5Xge.js} +10 -10
- package/build/site/assets/{splitPathsBySizeLoader-sZFydwLs.js → splitPathsBySizeLoader-DLaQMX-t.js} +1 -1
- package/build/site/index.html +1 -1
- package/build/types/build-plugin/__tests__/validateWidgetSet.test.d.ts +1 -0
- package/build/types/build-plugin/__tests__/validateWidgetSet.test.d.ts.map +1 -0
- package/build/types/build-plugin/buildWidgetSetManifest.d.ts.map +1 -1
- package/build/types/build-plugin/validateWidgetSet.d.ts +2 -0
- package/build/types/build-plugin/validateWidgetSet.d.ts.map +1 -0
- package/build/types/common/__tests__/standardizePathAndFileExtension.test.d.ts +1 -0
- package/build/types/common/__tests__/standardizePathAndFileExtension.test.d.ts.map +1 -0
- package/build/types/common/standardizePathAndFileExtension.d.ts +9 -0
- package/build/types/common/standardizePathAndFileExtension.d.ts.map +1 -0
- package/build/types/dev-plugin/FoundryWidgetDevPlugin.d.ts.map +1 -1
- package/build/types/dev-plugin/__tests__/validateDevEnvironment.test.d.ts +1 -0
- package/build/types/dev-plugin/__tests__/validateDevEnvironment.test.d.ts.map +1 -0
- package/build/types/dev-plugin/validateDevEnvironment.d.ts +2 -0
- package/build/types/dev-plugin/validateDevEnvironment.d.ts.map +1 -0
- package/package.json +5 -5
- package/build/esm/common/standardizeFileExtension.js.map +0 -1
- package/build/types/common/standardizeFileExtension.d.ts +0 -6
- package/build/types/common/standardizeFileExtension.d.ts.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# @osdk/widget.vite-plugin
|
|
2
2
|
|
|
3
|
+
## 3.2.5
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 71f33ca: Validate widget sets before building their manifest
|
|
8
|
+
- 71f33ca: Fix InvalidDevModeFilePath errors
|
|
9
|
+
- 71f33ca: warn when using wrong dev command
|
|
10
|
+
- 71f33ca: Fix Windows dev mode
|
|
11
|
+
- 71f33ca: Use Vite logger instead of console.warn in validateDevEnvironment
|
|
12
|
+
- Updated dependencies [a2df5ba]
|
|
13
|
+
- @osdk/widget.api@3.2.5
|
|
14
|
+
|
|
15
|
+
## 3.2.4
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- 3b43586: Support extension-less widget config import and improve reporting when config files not found
|
|
20
|
+
- 66896c7: Fix widget dev mode with tailwind causing project imports
|
|
21
|
+
- 3d7cb29: Propagate validation error cause in message for widget config validation
|
|
22
|
+
- @osdk/widget.api@3.2.4
|
|
23
|
+
|
|
3
24
|
## 3.2.3
|
|
4
25
|
|
|
5
26
|
### Patch Changes
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2025 Palantir Technologies, Inc. All rights reserved.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { describe, expect, it } from "vitest";
|
|
18
|
+
import { validateWidgetSet } from "../validateWidgetSet.js";
|
|
19
|
+
describe("validateWidgetSet", () => {
|
|
20
|
+
describe("validateWidgetIds", () => {
|
|
21
|
+
it("should not throw when all widget IDs are unique", () => {
|
|
22
|
+
const widgetBuilds = [createWidgetBuild("widget1"), createWidgetBuild("widget2"), createWidgetBuild("widget3")];
|
|
23
|
+
expect(() => validateWidgetSet(widgetBuilds)).not.toThrow();
|
|
24
|
+
});
|
|
25
|
+
it("should throw when duplicate widget IDs are found", () => {
|
|
26
|
+
const widgetBuilds = [createWidgetBuild("widget1"), createWidgetBuild("widget2"), createWidgetBuild("widget1")];
|
|
27
|
+
expect(() => validateWidgetSet(widgetBuilds)).toThrow("Duplicate widget ID: widget1. Each widget must have a unique ID.");
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
function createWidgetBuild(id) {
|
|
32
|
+
return {
|
|
33
|
+
widgetConfig: {
|
|
34
|
+
id,
|
|
35
|
+
name: `Widget ${id}`,
|
|
36
|
+
type: "workshop",
|
|
37
|
+
parameters: {},
|
|
38
|
+
events: {}
|
|
39
|
+
},
|
|
40
|
+
scripts: [],
|
|
41
|
+
stylesheets: []
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=validateWidgetSet.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validateWidgetSet.test.js","names":["describe","expect","it","validateWidgetSet","widgetBuilds","createWidgetBuild","not","toThrow","id","widgetConfig","name","type","parameters","events","scripts","stylesheets"],"sources":["validateWidgetSet.test.ts"],"sourcesContent":["/*\n * Copyright 2025 Palantir Technologies, Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { describe, expect, it } from \"vitest\";\nimport type { WidgetBuildOutputs } from \"../getWidgetBuildOutputs.js\";\nimport { validateWidgetSet } from \"../validateWidgetSet.js\";\n\ndescribe(\"validateWidgetSet\", () => {\n describe(\"validateWidgetIds\", () => {\n it(\"should not throw when all widget IDs are unique\", () => {\n const widgetBuilds: WidgetBuildOutputs[] = [\n createWidgetBuild(\"widget1\"),\n createWidgetBuild(\"widget2\"),\n createWidgetBuild(\"widget3\"),\n ];\n\n expect(() => validateWidgetSet(widgetBuilds)).not.toThrow();\n });\n\n it(\"should throw when duplicate widget IDs are found\", () => {\n const widgetBuilds: WidgetBuildOutputs[] = [\n createWidgetBuild(\"widget1\"),\n createWidgetBuild(\"widget2\"),\n createWidgetBuild(\"widget1\"),\n ];\n\n expect(() => validateWidgetSet(widgetBuilds)).toThrow(\n \"Duplicate widget ID: widget1. Each widget must have a unique ID.\",\n );\n });\n });\n});\n\nfunction createWidgetBuild(id: string): WidgetBuildOutputs {\n return {\n widgetConfig: {\n id,\n name: `Widget ${id}`,\n type: \"workshop\",\n parameters: {},\n events: {},\n },\n scripts: [],\n stylesheets: [],\n };\n}\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,SAASA,QAAQ,EAAEC,MAAM,EAAEC,EAAE,QAAQ,QAAQ;AAE7C,SAASC,iBAAiB,QAAQ,yBAAyB;AAE3DH,QAAQ,CAAC,mBAAmB,EAAE,MAAM;EAClCA,QAAQ,CAAC,mBAAmB,EAAE,MAAM;IAClCE,EAAE,CAAC,iDAAiD,EAAE,MAAM;MAC1D,MAAME,YAAkC,GAAG,CACzCC,iBAAiB,CAAC,SAAS,CAAC,EAC5BA,iBAAiB,CAAC,SAAS,CAAC,EAC5BA,iBAAiB,CAAC,SAAS,CAAC,CAC7B;MAEDJ,MAAM,CAAC,MAAME,iBAAiB,CAACC,YAAY,CAAC,CAAC,CAACE,GAAG,CAACC,OAAO,CAAC,CAAC;IAC7D,CAAC,CAAC;IAEFL,EAAE,CAAC,kDAAkD,EAAE,MAAM;MAC3D,MAAME,YAAkC,GAAG,CACzCC,iBAAiB,CAAC,SAAS,CAAC,EAC5BA,iBAAiB,CAAC,SAAS,CAAC,EAC5BA,iBAAiB,CAAC,SAAS,CAAC,CAC7B;MAEDJ,MAAM,CAAC,MAAME,iBAAiB,CAACC,YAAY,CAAC,CAAC,CAACG,OAAO,CACnD,kEACF,CAAC;IACH,CAAC,CAAC;EACJ,CAAC,CAAC;AACJ,CAAC,CAAC;AAEF,SAASF,iBAAiBA,CAACG,EAAU,EAAsB;EACzD,OAAO;IACLC,YAAY,EAAE;MACZD,EAAE;MACFE,IAAI,EAAE,UAAUF,EAAE,EAAE;MACpBG,IAAI,EAAE,UAAU;MAChBC,UAAU,EAAE,CAAC,CAAC;MACdC,MAAM,EAAE,CAAC;IACX,CAAC;IACDC,OAAO,EAAE,EAAE;IACXC,WAAW,EAAE;EACf,CAAC;AACH","ignoreList":[]}
|
|
@@ -14,7 +14,9 @@
|
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
+
import { validateWidgetSet } from "./validateWidgetSet.js";
|
|
17
18
|
export function buildWidgetSetManifest(widgetSetRid, widgetSetVersion, widgetBuilds, widgetSetInputSpec) {
|
|
19
|
+
validateWidgetSet(widgetBuilds);
|
|
18
20
|
return {
|
|
19
21
|
manifestVersion: "1.0.0",
|
|
20
22
|
widgetSet: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"buildWidgetSetManifest.js","names":["buildWidgetSetManifest","widgetSetRid","widgetSetVersion","widgetBuilds","widgetSetInputSpec","manifestVersion","widgetSet","rid","version","widgets","Object","fromEntries","map","buildWidgetManifest","widgetManifest","id","inputSpec","widgetBuild","widgetConfig","name","description","type","entrypointJs","scripts","script","path","trimLeadingSlash","src","scriptType","entrypointCss","stylesheets","parameters","convertParameters","events","entries","key","param","convertParameter","parameter","objectType","internalDoNotUseMetadata","Error","displayName","objectTypeRids","startsWith","slice"],"sources":["buildWidgetSetManifest.ts"],"sourcesContent":["/*\n * Copyright 2025 Palantir Technologies, Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type {\n ManifestParameterDefinition,\n ParameterDefinition,\n WidgetManifestConfig,\n WidgetSetInputSpec,\n WidgetSetManifest,\n} from \"@osdk/widget.api\";\nimport type { WidgetBuildOutputs } from \"./getWidgetBuildOutputs.js\";\n\nexport function buildWidgetSetManifest(\n widgetSetRid: string,\n widgetSetVersion: string,\n widgetBuilds: WidgetBuildOutputs[],\n widgetSetInputSpec: WidgetSetInputSpec,\n): WidgetSetManifest {\n return {\n manifestVersion: \"1.0.0\",\n widgetSet: {\n rid: widgetSetRid,\n version: widgetSetVersion,\n widgets: Object.fromEntries(\n widgetBuilds\n .map(buildWidgetManifest)\n .map((widgetManifest) => [widgetManifest.id, widgetManifest]),\n ),\n inputSpec: widgetSetInputSpec,\n },\n };\n}\n\nfunction buildWidgetManifest(\n widgetBuild: WidgetBuildOutputs,\n): WidgetManifestConfig {\n const widgetConfig = widgetBuild.widgetConfig;\n return {\n id: widgetConfig.id,\n name: widgetConfig.name,\n description: widgetConfig.description,\n type: \"workshopWidgetV1\",\n entrypointJs: widgetBuild.scripts.map((script) => ({\n path: trimLeadingSlash(script.src),\n type: script.scriptType,\n })),\n entrypointCss: widgetBuild.stylesheets.map((path) => ({\n path: trimLeadingSlash(path),\n })),\n parameters: convertParameters(widgetConfig.parameters),\n events: widgetConfig.events,\n };\n}\n\nfunction convertParameters(\n parameters: Record<string, ParameterDefinition>,\n): Record<string, ManifestParameterDefinition> {\n return Object.fromEntries(\n Object.entries(parameters).map(([key, param]) => [\n key,\n convertParameter(param),\n ]),\n );\n}\n\nfunction convertParameter(\n parameter: ParameterDefinition,\n): ManifestParameterDefinition {\n if (parameter.type === \"objectSet\") {\n // Config has already been validated so rid must be present\n if (parameter.objectType.internalDoNotUseMetadata == null) {\n throw new Error(\"Expected internal metadata to be present\");\n }\n return {\n type: \"objectSet\",\n displayName: parameter.displayName,\n objectTypeRids: [parameter.objectType.internalDoNotUseMetadata.rid],\n };\n }\n return parameter;\n}\n\nfunction trimLeadingSlash(path: string): string {\n return path.startsWith(\"/\") ? path.slice(1) : path;\n}\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;
|
|
1
|
+
{"version":3,"file":"buildWidgetSetManifest.js","names":["validateWidgetSet","buildWidgetSetManifest","widgetSetRid","widgetSetVersion","widgetBuilds","widgetSetInputSpec","manifestVersion","widgetSet","rid","version","widgets","Object","fromEntries","map","buildWidgetManifest","widgetManifest","id","inputSpec","widgetBuild","widgetConfig","name","description","type","entrypointJs","scripts","script","path","trimLeadingSlash","src","scriptType","entrypointCss","stylesheets","parameters","convertParameters","events","entries","key","param","convertParameter","parameter","objectType","internalDoNotUseMetadata","Error","displayName","objectTypeRids","startsWith","slice"],"sources":["buildWidgetSetManifest.ts"],"sourcesContent":["/*\n * Copyright 2025 Palantir Technologies, Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type {\n ManifestParameterDefinition,\n ParameterDefinition,\n WidgetManifestConfig,\n WidgetSetInputSpec,\n WidgetSetManifest,\n} from \"@osdk/widget.api\";\nimport type { WidgetBuildOutputs } from \"./getWidgetBuildOutputs.js\";\nimport { validateWidgetSet } from \"./validateWidgetSet.js\";\n\nexport function buildWidgetSetManifest(\n widgetSetRid: string,\n widgetSetVersion: string,\n widgetBuilds: WidgetBuildOutputs[],\n widgetSetInputSpec: WidgetSetInputSpec,\n): WidgetSetManifest {\n validateWidgetSet(widgetBuilds);\n return {\n manifestVersion: \"1.0.0\",\n widgetSet: {\n rid: widgetSetRid,\n version: widgetSetVersion,\n widgets: Object.fromEntries(\n widgetBuilds\n .map(buildWidgetManifest)\n .map((widgetManifest) => [widgetManifest.id, widgetManifest]),\n ),\n inputSpec: widgetSetInputSpec,\n },\n };\n}\n\nfunction buildWidgetManifest(\n widgetBuild: WidgetBuildOutputs,\n): WidgetManifestConfig {\n const widgetConfig = widgetBuild.widgetConfig;\n return {\n id: widgetConfig.id,\n name: widgetConfig.name,\n description: widgetConfig.description,\n type: \"workshopWidgetV1\",\n entrypointJs: widgetBuild.scripts.map((script) => ({\n path: trimLeadingSlash(script.src),\n type: script.scriptType,\n })),\n entrypointCss: widgetBuild.stylesheets.map((path) => ({\n path: trimLeadingSlash(path),\n })),\n parameters: convertParameters(widgetConfig.parameters),\n events: widgetConfig.events,\n };\n}\n\nfunction convertParameters(\n parameters: Record<string, ParameterDefinition>,\n): Record<string, ManifestParameterDefinition> {\n return Object.fromEntries(\n Object.entries(parameters).map(([key, param]) => [\n key,\n convertParameter(param),\n ]),\n );\n}\n\nfunction convertParameter(\n parameter: ParameterDefinition,\n): ManifestParameterDefinition {\n if (parameter.type === \"objectSet\") {\n // Config has already been validated so rid must be present\n if (parameter.objectType.internalDoNotUseMetadata == null) {\n throw new Error(\"Expected internal metadata to be present\");\n }\n return {\n type: \"objectSet\",\n displayName: parameter.displayName,\n objectTypeRids: [parameter.objectType.internalDoNotUseMetadata.rid],\n };\n }\n return parameter;\n}\n\nfunction trimLeadingSlash(path: string): string {\n return path.startsWith(\"/\") ? path.slice(1) : path;\n}\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAUA,SAASA,iBAAiB,QAAQ,wBAAwB;AAE1D,OAAO,SAASC,sBAAsBA,CACpCC,YAAoB,EACpBC,gBAAwB,EACxBC,YAAkC,EAClCC,kBAAsC,EACnB;EACnBL,iBAAiB,CAACI,YAAY,CAAC;EAC/B,OAAO;IACLE,eAAe,EAAE,OAAO;IACxBC,SAAS,EAAE;MACTC,GAAG,EAAEN,YAAY;MACjBO,OAAO,EAAEN,gBAAgB;MACzBO,OAAO,EAAEC,MAAM,CAACC,WAAW,CACzBR,YAAY,CACTS,GAAG,CAACC,mBAAmB,CAAC,CACxBD,GAAG,CAAEE,cAAc,IAAK,CAACA,cAAc,CAACC,EAAE,EAAED,cAAc,CAAC,CAChE,CAAC;MACDE,SAAS,EAAEZ;IACb;EACF,CAAC;AACH;AAEA,SAASS,mBAAmBA,CAC1BI,WAA+B,EACT;EACtB,MAAMC,YAAY,GAAGD,WAAW,CAACC,YAAY;EAC7C,OAAO;IACLH,EAAE,EAAEG,YAAY,CAACH,EAAE;IACnBI,IAAI,EAAED,YAAY,CAACC,IAAI;IACvBC,WAAW,EAAEF,YAAY,CAACE,WAAW;IACrCC,IAAI,EAAE,kBAAkB;IACxBC,YAAY,EAAEL,WAAW,CAACM,OAAO,CAACX,GAAG,CAAEY,MAAM,KAAM;MACjDC,IAAI,EAAEC,gBAAgB,CAACF,MAAM,CAACG,GAAG,CAAC;MAClCN,IAAI,EAAEG,MAAM,CAACI;IACf,CAAC,CAAC,CAAC;IACHC,aAAa,EAAEZ,WAAW,CAACa,WAAW,CAAClB,GAAG,CAAEa,IAAI,KAAM;MACpDA,IAAI,EAAEC,gBAAgB,CAACD,IAAI;IAC7B,CAAC,CAAC,CAAC;IACHM,UAAU,EAAEC,iBAAiB,CAACd,YAAY,CAACa,UAAU,CAAC;IACtDE,MAAM,EAAEf,YAAY,CAACe;EACvB,CAAC;AACH;AAEA,SAASD,iBAAiBA,CACxBD,UAA+C,EACF;EAC7C,OAAOrB,MAAM,CAACC,WAAW,CACvBD,MAAM,CAACwB,OAAO,CAACH,UAAU,CAAC,CAACnB,GAAG,CAAC,CAAC,CAACuB,GAAG,EAAEC,KAAK,CAAC,KAAK,CAC/CD,GAAG,EACHE,gBAAgB,CAACD,KAAK,CAAC,CACxB,CACH,CAAC;AACH;AAEA,SAASC,gBAAgBA,CACvBC,SAA8B,EACD;EAC7B,IAAIA,SAAS,CAACjB,IAAI,KAAK,WAAW,EAAE;IAClC;IACA,IAAIiB,SAAS,CAACC,UAAU,CAACC,wBAAwB,IAAI,IAAI,EAAE;MACzD,MAAM,IAAIC,KAAK,CAAC,0CAA0C,CAAC;IAC7D;IACA,OAAO;MACLpB,IAAI,EAAE,WAAW;MACjBqB,WAAW,EAAEJ,SAAS,CAACI,WAAW;MAClCC,cAAc,EAAE,CAACL,SAAS,CAACC,UAAU,CAACC,wBAAwB,CAACjC,GAAG;IACpE,CAAC;EACH;EACA,OAAO+B,SAAS;AAClB;AAEA,SAASZ,gBAAgBA,CAACD,IAAY,EAAU;EAC9C,OAAOA,IAAI,CAACmB,UAAU,CAAC,GAAG,CAAC,GAAGnB,IAAI,CAACoB,KAAK,CAAC,CAAC,CAAC,GAAGpB,IAAI;AACpD","ignoreList":[]}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2025 Palantir Technologies, Inc. All rights reserved.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
export function validateWidgetSet(widgetBuilds) {
|
|
18
|
+
validateWidgetIds(widgetBuilds);
|
|
19
|
+
}
|
|
20
|
+
function validateWidgetIds(widgetBuilds) {
|
|
21
|
+
const widgetIds = new Set();
|
|
22
|
+
for (const widgetBuild of widgetBuilds) {
|
|
23
|
+
const widgetConfigId = widgetBuild.widgetConfig.id;
|
|
24
|
+
if (widgetIds.has(widgetConfigId)) {
|
|
25
|
+
throw new Error(`Duplicate widget ID: ${widgetConfigId}. Each widget must have a unique ID.`);
|
|
26
|
+
}
|
|
27
|
+
widgetIds.add(widgetConfigId);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=validateWidgetSet.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validateWidgetSet.js","names":["validateWidgetSet","widgetBuilds","validateWidgetIds","widgetIds","Set","widgetBuild","widgetConfigId","widgetConfig","id","has","Error","add"],"sources":["validateWidgetSet.ts"],"sourcesContent":["/*\n * Copyright 2025 Palantir Technologies, Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { WidgetBuildOutputs } from \"./getWidgetBuildOutputs.js\";\n\nexport function validateWidgetSet(widgetBuilds: WidgetBuildOutputs[]): void {\n validateWidgetIds(widgetBuilds);\n}\n\nfunction validateWidgetIds(widgetBuilds: WidgetBuildOutputs[]): void {\n const widgetIds = new Set<string>();\n for (const widgetBuild of widgetBuilds) {\n const widgetConfigId = widgetBuild.widgetConfig.id;\n if (widgetIds.has(widgetConfigId)) {\n throw new Error(\n `Duplicate widget ID: ${widgetConfigId}. Each widget must have a unique ID.`,\n );\n }\n widgetIds.add(widgetConfigId);\n }\n}\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAIA,OAAO,SAASA,iBAAiBA,CAACC,YAAkC,EAAQ;EAC1EC,iBAAiB,CAACD,YAAY,CAAC;AACjC;AAEA,SAASC,iBAAiBA,CAACD,YAAkC,EAAQ;EACnE,MAAME,SAAS,GAAG,IAAIC,GAAG,CAAS,CAAC;EACnC,KAAK,MAAMC,WAAW,IAAIJ,YAAY,EAAE;IACtC,MAAMK,cAAc,GAAGD,WAAW,CAACE,YAAY,CAACC,EAAE;IAClD,IAAIL,SAAS,CAACM,GAAG,CAACH,cAAc,CAAC,EAAE;MACjC,MAAM,IAAII,KAAK,CACb,wBAAwBJ,cAAc,sCACxC,CAAC;IACH;IACAH,SAAS,CAACQ,GAAG,CAACL,cAAc,CAAC;EAC/B;AACF","ignoreList":[]}
|
package/build/esm/client/app.js
CHANGED
|
@@ -30,6 +30,7 @@ export const App = () => {
|
|
|
30
30
|
const [pageState, setPageState] = React.useState({
|
|
31
31
|
state: "loading"
|
|
32
32
|
});
|
|
33
|
+
const numAttempts = React.useRef(0);
|
|
33
34
|
|
|
34
35
|
// Load entrypoints values on mount
|
|
35
36
|
useEffect(() => {
|
|
@@ -39,8 +40,9 @@ export const App = () => {
|
|
|
39
40
|
// Poll the finish endpoint until it returns a success or error
|
|
40
41
|
useEffect(() => {
|
|
41
42
|
const poll = window.setInterval(() => {
|
|
42
|
-
void finish().then(result => {
|
|
43
|
+
void finish(numAttempts.current).then(result => {
|
|
43
44
|
if (result.status === "pending") {
|
|
45
|
+
numAttempts.current++;
|
|
44
46
|
return;
|
|
45
47
|
}
|
|
46
48
|
if (result.status === "error") {
|
|
@@ -108,7 +110,7 @@ export const App = () => {
|
|
|
108
110
|
function loadEntrypoints() {
|
|
109
111
|
return fetch("../entrypoints").then(res => res.json());
|
|
110
112
|
}
|
|
111
|
-
function finish() {
|
|
112
|
-
return fetch(
|
|
113
|
+
function finish(attempt) {
|
|
114
|
+
return fetch(`../finish?attempt=${attempt}`).then(res => res.json());
|
|
113
115
|
}
|
|
114
116
|
//# sourceMappingURL=app.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app.js","names":["NonIdealState","Pre","Spinner","SpinnerSize","React","useEffect","EntrypointIframe","POLLING_INTERVAL","REDIRECT_DELAY","ResponseError","Error","constructor","message","response","App","entrypointPaths","setEntrypointPaths","useState","pageState","setPageState","state","loadEntrypoints","then","poll","window","setInterval","finish","result","status","error","clearInterval","isRedirecting","redirectUrl","setTimeout","location","href","catch","console","undefined","createElement","className","title","icon","intent","description","size","SMALL","Fragment","map","entrypointPath","src","key","fetch","res","json"],"sources":["app.tsx"],"sourcesContent":["/*\n * Copyright 2024 Palantir Technologies, Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { NonIdealState, Pre, Spinner, SpinnerSize } from \"@blueprintjs/core\";\nimport React, { useEffect } from \"react\";\nimport { EntrypointIframe } from \"./entrypointIframe.js\";\n\ntype PageState =\n | {\n state: \"loading\";\n }\n | {\n state: \"failed\";\n error?: string;\n response?: string;\n }\n | {\n state: \"success\";\n isRedirecting: boolean;\n };\n\nconst POLLING_INTERVAL = 250;\nconst REDIRECT_DELAY = 500;\n\nclass ResponseError extends Error {\n public readonly response: string;\n\n constructor(message: string, response: string) {\n super(message);\n this.response = response;\n }\n}\n\nexport const App: React.FC = () => {\n const [entrypointPaths, setEntrypointPaths] = React.useState<string[]>([]);\n const [pageState, setPageState] = React.useState<PageState>({\n state: \"loading\",\n });\n\n // Load entrypoints values on mount\n useEffect(() => {\n void loadEntrypoints().then(setEntrypointPaths);\n }, []);\n\n // Poll the finish endpoint until it returns a success or error\n useEffect(() => {\n const poll = window.setInterval(() => {\n void finish()\n .then((result) => {\n if (result.status === \"pending\") {\n return;\n }\n if (result.status === \"error\") {\n if (result.response != null) {\n throw new ResponseError(result.error, result.response);\n }\n throw new Error(result.error);\n }\n\n // On success, we clear the poll and end the loading state\n window.clearInterval(poll);\n setPageState({\n state: \"success\",\n isRedirecting: result.redirectUrl != null,\n });\n\n // When running in Code Workspaces the parent app will handle the redirect\n if (result.status === \"success\" && result.redirectUrl != null) {\n setTimeout(() => {\n window.location.href = result.redirectUrl!;\n }, REDIRECT_DELAY);\n }\n })\n .catch((error: unknown) => {\n window.clearInterval(poll);\n // eslint-disable-next-line no-console\n console.error(\"Failed to finish dev mode setup:\", error);\n setPageState({\n state: \"failed\",\n error: error instanceof Error ? error.message : undefined,\n response: error instanceof ResponseError\n ? error.response\n : undefined,\n });\n });\n }, POLLING_INTERVAL);\n return () => window.clearInterval(poll);\n }, []);\n\n return (\n <div className=\"body\">\n {pageState.state === \"loading\" && (\n <NonIdealState\n title=\"Generating developer mode manifest…\"\n icon={<Spinner intent=\"primary\" />}\n />\n )}\n {pageState.state === \"success\" && (\n <NonIdealState\n title=\"Started dev mode\"\n icon=\"tick-circle\"\n description={\n <div className=\"description\">\n <Spinner intent=\"primary\" size={SpinnerSize.SMALL} />{\" \"}\n {pageState.isRedirecting\n ? <span>Redirecting you…</span>\n : <span>Loading preview…</span>}\n </div>\n }\n />\n )}\n {pageState.state === \"failed\" && (\n <NonIdealState\n title=\"Failed to start dev mode\"\n icon=\"error\"\n description={\n <>\n {pageState.response != null && (\n <Pre className=\"response-block\">{pageState.response}</Pre>\n )}\n <Pre className=\"response-block\">{pageState.error}</Pre>\n </>\n }\n />\n )}\n {/* To load the entrypoint info, we have to actually load it in the browser to get vite to follow the module graph. Since we know these files will fail, we just load them in iframes set to display: none to trigger the load hook in vite */}\n {entrypointPaths.map((entrypointPath) => (\n <EntrypointIframe src={entrypointPath} key={entrypointPath} />\n ))}\n </div>\n );\n};\n\nfunction loadEntrypoints(): Promise<string[]> {\n return fetch(\"../entrypoints\").then((res) => res.json());\n}\n\nfunction finish(): Promise<\n | {\n status: \"success\";\n redirectUrl: string | null;\n }\n | {\n status: \"error\";\n error: string;\n response?: string;\n }\n | {\n status: \"pending\";\n }\n> {\n return fetch(
|
|
1
|
+
{"version":3,"file":"app.js","names":["NonIdealState","Pre","Spinner","SpinnerSize","React","useEffect","EntrypointIframe","POLLING_INTERVAL","REDIRECT_DELAY","ResponseError","Error","constructor","message","response","App","entrypointPaths","setEntrypointPaths","useState","pageState","setPageState","state","numAttempts","useRef","loadEntrypoints","then","poll","window","setInterval","finish","current","result","status","error","clearInterval","isRedirecting","redirectUrl","setTimeout","location","href","catch","console","undefined","createElement","className","title","icon","intent","description","size","SMALL","Fragment","map","entrypointPath","src","key","fetch","res","json","attempt"],"sources":["app.tsx"],"sourcesContent":["/*\n * Copyright 2024 Palantir Technologies, Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { NonIdealState, Pre, Spinner, SpinnerSize } from \"@blueprintjs/core\";\nimport React, { useEffect } from \"react\";\nimport { EntrypointIframe } from \"./entrypointIframe.js\";\n\ntype PageState =\n | {\n state: \"loading\";\n }\n | {\n state: \"failed\";\n error?: string;\n response?: string;\n }\n | {\n state: \"success\";\n isRedirecting: boolean;\n };\n\nconst POLLING_INTERVAL = 250;\nconst REDIRECT_DELAY = 500;\n\nclass ResponseError extends Error {\n public readonly response: string;\n\n constructor(message: string, response: string) {\n super(message);\n this.response = response;\n }\n}\n\nexport const App: React.FC = () => {\n const [entrypointPaths, setEntrypointPaths] = React.useState<string[]>([]);\n const [pageState, setPageState] = React.useState<PageState>({\n state: \"loading\",\n });\n const numAttempts = React.useRef(0);\n\n // Load entrypoints values on mount\n useEffect(() => {\n void loadEntrypoints().then(setEntrypointPaths);\n }, []);\n\n // Poll the finish endpoint until it returns a success or error\n useEffect(() => {\n const poll = window.setInterval(() => {\n void finish(numAttempts.current)\n .then((result) => {\n if (result.status === \"pending\") {\n numAttempts.current++;\n return;\n }\n if (result.status === \"error\") {\n if (result.response != null) {\n throw new ResponseError(result.error, result.response);\n }\n throw new Error(result.error);\n }\n\n // On success, we clear the poll and end the loading state\n window.clearInterval(poll);\n setPageState({\n state: \"success\",\n isRedirecting: result.redirectUrl != null,\n });\n\n // When running in Code Workspaces the parent app will handle the redirect\n if (result.status === \"success\" && result.redirectUrl != null) {\n setTimeout(() => {\n window.location.href = result.redirectUrl!;\n }, REDIRECT_DELAY);\n }\n })\n .catch((error: unknown) => {\n window.clearInterval(poll);\n // eslint-disable-next-line no-console\n console.error(\"Failed to finish dev mode setup:\", error);\n setPageState({\n state: \"failed\",\n error: error instanceof Error ? error.message : undefined,\n response: error instanceof ResponseError\n ? error.response\n : undefined,\n });\n });\n }, POLLING_INTERVAL);\n return () => window.clearInterval(poll);\n }, []);\n\n return (\n <div className=\"body\">\n {pageState.state === \"loading\" && (\n <NonIdealState\n title=\"Generating developer mode manifest…\"\n icon={<Spinner intent=\"primary\" />}\n />\n )}\n {pageState.state === \"success\" && (\n <NonIdealState\n title=\"Started dev mode\"\n icon=\"tick-circle\"\n description={\n <div className=\"description\">\n <Spinner intent=\"primary\" size={SpinnerSize.SMALL} />{\" \"}\n {pageState.isRedirecting\n ? <span>Redirecting you…</span>\n : <span>Loading preview…</span>}\n </div>\n }\n />\n )}\n {pageState.state === \"failed\" && (\n <NonIdealState\n title=\"Failed to start dev mode\"\n icon=\"error\"\n description={\n <>\n {pageState.response != null && (\n <Pre className=\"response-block\">{pageState.response}</Pre>\n )}\n <Pre className=\"response-block\">{pageState.error}</Pre>\n </>\n }\n />\n )}\n {/* To load the entrypoint info, we have to actually load it in the browser to get vite to follow the module graph. Since we know these files will fail, we just load them in iframes set to display: none to trigger the load hook in vite */}\n {entrypointPaths.map((entrypointPath) => (\n <EntrypointIframe src={entrypointPath} key={entrypointPath} />\n ))}\n </div>\n );\n};\n\nfunction loadEntrypoints(): Promise<string[]> {\n return fetch(\"../entrypoints\").then((res) => res.json());\n}\n\nfunction finish(attempt: number): Promise<\n | {\n status: \"success\";\n redirectUrl: string | null;\n }\n | {\n status: \"error\";\n error: string;\n response?: string;\n }\n | {\n status: \"pending\";\n }\n> {\n return fetch(`../finish?attempt=${attempt}`).then((res) => res.json());\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -46,7 +46,7 @@ describe("extractWidgetConfig", () => {
|
|
|
46
46
|
throw new Error(`Widget id "${config.id}" does not match allowed pattern (must be camelCase)`);
|
|
47
47
|
});
|
|
48
48
|
await expect(extractWidgetConfig("/path/to/config.ts", MOCK_SERVER)).rejects.toThrow(expect.objectContaining({
|
|
49
|
-
message: "
|
|
49
|
+
message: "Encountered error: 'Widget id \"Invalid-Id\" does not match allowed pattern (must be camelCase)' while loading widget config from /path/to/config.ts",
|
|
50
50
|
cause: expect.objectContaining({
|
|
51
51
|
message: `Widget id "Invalid-Id" does not match allowed pattern (must be camelCase)`
|
|
52
52
|
})
|
|
@@ -59,7 +59,7 @@ describe("extractWidgetConfig", () => {
|
|
|
59
59
|
}
|
|
60
60
|
});
|
|
61
61
|
await expect(extractWidgetConfig("/path/to/config.ts", MOCK_SERVER)).rejects.toThrow(expect.objectContaining({
|
|
62
|
-
message: "
|
|
62
|
+
message: "Encountered error: 'No default export found in /path/to/config.ts' while loading widget config from /path/to/config.ts",
|
|
63
63
|
cause: expect.objectContaining({
|
|
64
64
|
message: "No default export found in /path/to/config.ts"
|
|
65
65
|
})
|
|
@@ -67,7 +67,7 @@ describe("extractWidgetConfig", () => {
|
|
|
67
67
|
});
|
|
68
68
|
test("throws for invalid module path", async () => {
|
|
69
69
|
vi.mocked(MOCK_SERVER.ssrLoadModule).mockRejectedValue(new Error("Module loading failed"));
|
|
70
|
-
await expect(extractWidgetConfig("/invalid/path/config.ts", MOCK_SERVER)).rejects.toThrow("
|
|
70
|
+
await expect(extractWidgetConfig("/invalid/path/config.ts", MOCK_SERVER)).rejects.toThrow("Encountered error: 'Module loading failed' while loading widget config from /invalid/path/config.ts");
|
|
71
71
|
});
|
|
72
72
|
});
|
|
73
73
|
const MOCK_CONFIG = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extractWidgetConfig.test.js","names":["beforeEach","describe","expect","test","vi","extractWidgetConfig","validateWidgetConfigModule","MOCK_SERVER","ssrLoadModule","fn","restoreAllMocks","mocked","mockResolvedValue","default","MOCK_CONFIG","validateSpy","spyOn","result","toEqual","toHaveBeenCalledWith","id","name","type","parameters","mockImplementation","config","Error","rejects","toThrow","objectContaining","message","cause","notDefault","mockRejectedValue","description","paramOne","displayName","paramTwo","events","updateParameters","parameterUpdateIds"],"sources":["extractWidgetConfig.test.ts"],"sourcesContent":["/*\n * Copyright 2025 Palantir Technologies, Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { ViteDevServer } from \"vite\";\nimport { beforeEach, describe, expect, test, vi } from \"vitest\";\nimport { extractWidgetConfig } from \"../extractWidgetConfig.js\";\nimport * as validateWidgetConfigModule from \"../validateWidgetConfig.js\";\n\nconst MOCK_SERVER = {\n ssrLoadModule: vi.fn(),\n} as unknown as ViteDevServer;\n\ndescribe(\"extractWidgetConfig\", () => {\n beforeEach(() => {\n vi.restoreAllMocks();\n });\n\n test(\"extracts valid widget configuration\", async () => {\n vi.mocked(MOCK_SERVER.ssrLoadModule).mockResolvedValue({\n default: MOCK_CONFIG,\n });\n\n const validateSpy = vi.spyOn(\n validateWidgetConfigModule,\n \"validateWidgetConfig\",\n );\n\n const result = await extractWidgetConfig(\"/path/to/config.ts\", MOCK_SERVER);\n expect(result).toEqual(MOCK_CONFIG);\n expect(validateSpy).toHaveBeenCalledWith(MOCK_CONFIG);\n });\n\n test(\"throws for invalid widget configuration\", async () => {\n const INVALID_CONFIG = {\n id: \"Invalid-Id\",\n name: \"Test Widget\",\n type: \"workshop\",\n parameters: {},\n };\n\n vi.mocked(MOCK_SERVER.ssrLoadModule).mockResolvedValue({\n default: INVALID_CONFIG,\n });\n\n vi.spyOn(validateWidgetConfigModule, \"validateWidgetConfig\")\n .mockImplementation(config => {\n throw new Error(\n `Widget id \"${config.id}\" does not match allowed pattern (must be camelCase)`,\n );\n });\n\n await expect(extractWidgetConfig(\"/path/to/config.ts\", MOCK_SERVER))\n .rejects.toThrow(\n expect.objectContaining({\n message:
|
|
1
|
+
{"version":3,"file":"extractWidgetConfig.test.js","names":["beforeEach","describe","expect","test","vi","extractWidgetConfig","validateWidgetConfigModule","MOCK_SERVER","ssrLoadModule","fn","restoreAllMocks","mocked","mockResolvedValue","default","MOCK_CONFIG","validateSpy","spyOn","result","toEqual","toHaveBeenCalledWith","id","name","type","parameters","mockImplementation","config","Error","rejects","toThrow","objectContaining","message","cause","notDefault","mockRejectedValue","description","paramOne","displayName","paramTwo","events","updateParameters","parameterUpdateIds"],"sources":["extractWidgetConfig.test.ts"],"sourcesContent":["/*\n * Copyright 2025 Palantir Technologies, Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { ViteDevServer } from \"vite\";\nimport { beforeEach, describe, expect, test, vi } from \"vitest\";\nimport { extractWidgetConfig } from \"../extractWidgetConfig.js\";\nimport * as validateWidgetConfigModule from \"../validateWidgetConfig.js\";\n\nconst MOCK_SERVER = {\n ssrLoadModule: vi.fn(),\n} as unknown as ViteDevServer;\n\ndescribe(\"extractWidgetConfig\", () => {\n beforeEach(() => {\n vi.restoreAllMocks();\n });\n\n test(\"extracts valid widget configuration\", async () => {\n vi.mocked(MOCK_SERVER.ssrLoadModule).mockResolvedValue({\n default: MOCK_CONFIG,\n });\n\n const validateSpy = vi.spyOn(\n validateWidgetConfigModule,\n \"validateWidgetConfig\",\n );\n\n const result = await extractWidgetConfig(\"/path/to/config.ts\", MOCK_SERVER);\n expect(result).toEqual(MOCK_CONFIG);\n expect(validateSpy).toHaveBeenCalledWith(MOCK_CONFIG);\n });\n\n test(\"throws for invalid widget configuration\", async () => {\n const INVALID_CONFIG = {\n id: \"Invalid-Id\",\n name: \"Test Widget\",\n type: \"workshop\",\n parameters: {},\n };\n\n vi.mocked(MOCK_SERVER.ssrLoadModule).mockResolvedValue({\n default: INVALID_CONFIG,\n });\n\n vi.spyOn(validateWidgetConfigModule, \"validateWidgetConfig\")\n .mockImplementation(config => {\n throw new Error(\n `Widget id \"${config.id}\" does not match allowed pattern (must be camelCase)`,\n );\n });\n\n await expect(extractWidgetConfig(\"/path/to/config.ts\", MOCK_SERVER))\n .rejects.toThrow(\n expect.objectContaining({\n message:\n \"Encountered error: 'Widget id \\\"Invalid-Id\\\" does not match allowed pattern (must be camelCase)' while loading widget config from /path/to/config.ts\",\n cause: expect.objectContaining({\n message:\n `Widget id \"Invalid-Id\" does not match allowed pattern (must be camelCase)`,\n }),\n }),\n );\n });\n\n test(\"throws for missing default export\", async () => {\n vi.mocked(MOCK_SERVER.ssrLoadModule).mockResolvedValue({\n notDefault: { id: \"test\" },\n });\n\n await expect(extractWidgetConfig(\"/path/to/config.ts\", MOCK_SERVER))\n .rejects.toThrow(\n expect.objectContaining({\n message:\n \"Encountered error: 'No default export found in /path/to/config.ts' while loading widget config from /path/to/config.ts\",\n cause: expect.objectContaining({\n message: \"No default export found in /path/to/config.ts\",\n }),\n }),\n );\n });\n\n test(\"throws for invalid module path\", async () => {\n vi.mocked(MOCK_SERVER.ssrLoadModule).mockRejectedValue(\n new Error(\"Module loading failed\"),\n );\n\n await expect(extractWidgetConfig(\"/invalid/path/config.ts\", MOCK_SERVER))\n .rejects.toThrow(\n \"Encountered error: 'Module loading failed' while loading widget config from /invalid/path/config.ts\",\n );\n });\n});\n\nconst MOCK_CONFIG = {\n id: \"testWidget\",\n name: \"Test Widget\",\n description: \"A test widget\",\n type: \"workshop\",\n parameters: {\n paramOne: {\n displayName: \"Parameter One\",\n type: \"string\",\n },\n paramTwo: {\n displayName: \"Parameter Two\",\n type: \"string\",\n },\n },\n events: {\n updateParameters: {\n displayName: \"Update Parameters\",\n parameterUpdateIds: [\"paramOne\", \"paramTwo\"],\n },\n },\n};\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2025 Palantir Technologies, Inc. All rights reserved.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { describe, expect, it } from "vitest";
|
|
18
|
+
import { standardizePathAndFileExtension } from "../standardizePathAndFileExtension.js";
|
|
19
|
+
describe("standardizePathAndFileExtension", () => {
|
|
20
|
+
describe("file extension standardization", () => {
|
|
21
|
+
it("should replace .ts extension with .js", () => {
|
|
22
|
+
expect(standardizePathAndFileExtension("/path/to/file.ts")).toBe("/path/to/file.js");
|
|
23
|
+
});
|
|
24
|
+
it("should replace .tsx extension with .js", () => {
|
|
25
|
+
expect(standardizePathAndFileExtension("/path/to/component.tsx")).toBe("/path/to/component.js");
|
|
26
|
+
});
|
|
27
|
+
it("should replace .jsx extension with .js", () => {
|
|
28
|
+
expect(standardizePathAndFileExtension("/path/to/component.jsx")).toBe("/path/to/component.js");
|
|
29
|
+
});
|
|
30
|
+
it("should keep .js extension as is", () => {
|
|
31
|
+
expect(standardizePathAndFileExtension("/path/to/file.js")).toBe("/path/to/file.js");
|
|
32
|
+
});
|
|
33
|
+
it("should append .js to config files", () => {
|
|
34
|
+
expect(standardizePathAndFileExtension("/path/to/widget.config")).toBe("/path/to/widget.config.js");
|
|
35
|
+
});
|
|
36
|
+
it("should handle files without extensions", () => {
|
|
37
|
+
expect(standardizePathAndFileExtension("/path/to/file")).toBe("/path/to/file");
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
describe("path normalization", () => {
|
|
41
|
+
it("should normalize Windows paths with backslashes to forward slashes", () => {
|
|
42
|
+
expect(standardizePathAndFileExtension("C:\\Users\\project\\src\\file.ts")).toBe("C:/Users/project/src/file.js");
|
|
43
|
+
});
|
|
44
|
+
it("should normalize Windows paths with mixed separators", () => {
|
|
45
|
+
expect(standardizePathAndFileExtension("C:\\Users/project\\src/file.tsx")).toBe("C:/Users/project/src/file.js");
|
|
46
|
+
});
|
|
47
|
+
it("should preserve paths that already use forward slashes", () => {
|
|
48
|
+
expect(standardizePathAndFileExtension("/Users/project/src/file.ts")).toBe("/Users/project/src/file.js");
|
|
49
|
+
});
|
|
50
|
+
it("should handle Windows config file paths", () => {
|
|
51
|
+
expect(standardizePathAndFileExtension("D:\\workspace\\widgets\\my-widget.config")).toBe("D:/workspace/widgets/my-widget.config.js");
|
|
52
|
+
});
|
|
53
|
+
it("should handle Windows paths without file extensions", () => {
|
|
54
|
+
expect(standardizePathAndFileExtension("C:\\Users\\project\\README")).toBe("C:/Users/project/README");
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
describe("edge cases", () => {
|
|
58
|
+
it("should handle empty string", () => {
|
|
59
|
+
expect(standardizePathAndFileExtension("")).toBe("");
|
|
60
|
+
});
|
|
61
|
+
it("should handle paths with multiple dots", () => {
|
|
62
|
+
expect(standardizePathAndFileExtension("/path/to/file.test.ts")).toBe("/path/to/file.test.js");
|
|
63
|
+
});
|
|
64
|
+
it("should handle Windows paths with multiple dots", () => {
|
|
65
|
+
expect(standardizePathAndFileExtension("C:\\path\\to\\file.test.tsx")).toBe("C:/path/to/file.test.js");
|
|
66
|
+
});
|
|
67
|
+
it("should handle virtual paths with @fs prefix", () => {
|
|
68
|
+
expect(standardizePathAndFileExtension("/@fs/C:\\Users\\project\\src\\file.ts")).toBe("/@fs/C:/Users/project/src/file.js");
|
|
69
|
+
});
|
|
70
|
+
it("should handle paths with /src/ directory for Windows", () => {
|
|
71
|
+
const result = standardizePathAndFileExtension("C:\\projects\\my-app\\src\\components\\Widget.tsx");
|
|
72
|
+
expect(result).toBe("C:/projects/my-app/src/components/Widget.js");
|
|
73
|
+
expect(result.includes("/src/")).toBe(true);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
//# sourceMappingURL=standardizePathAndFileExtension.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"standardizePathAndFileExtension.test.js","names":["describe","expect","it","standardizePathAndFileExtension","toBe","result","includes"],"sources":["standardizePathAndFileExtension.test.ts"],"sourcesContent":["/*\n * Copyright 2025 Palantir Technologies, Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { describe, expect, it } from \"vitest\";\nimport { standardizePathAndFileExtension } from \"../standardizePathAndFileExtension.js\";\n\ndescribe(\"standardizePathAndFileExtension\", () => {\n describe(\"file extension standardization\", () => {\n it(\"should replace .ts extension with .js\", () => {\n expect(standardizePathAndFileExtension(\"/path/to/file.ts\")).toBe(\n \"/path/to/file.js\",\n );\n });\n\n it(\"should replace .tsx extension with .js\", () => {\n expect(standardizePathAndFileExtension(\"/path/to/component.tsx\")).toBe(\n \"/path/to/component.js\",\n );\n });\n\n it(\"should replace .jsx extension with .js\", () => {\n expect(standardizePathAndFileExtension(\"/path/to/component.jsx\")).toBe(\n \"/path/to/component.js\",\n );\n });\n\n it(\"should keep .js extension as is\", () => {\n expect(standardizePathAndFileExtension(\"/path/to/file.js\")).toBe(\n \"/path/to/file.js\",\n );\n });\n\n it(\"should append .js to config files\", () => {\n expect(standardizePathAndFileExtension(\"/path/to/widget.config\")).toBe(\n \"/path/to/widget.config.js\",\n );\n });\n\n it(\"should handle files without extensions\", () => {\n expect(standardizePathAndFileExtension(\"/path/to/file\")).toBe(\n \"/path/to/file\",\n );\n });\n });\n\n describe(\"path normalization\", () => {\n it(\"should normalize Windows paths with backslashes to forward slashes\", () => {\n expect(\n standardizePathAndFileExtension(\"C:\\\\Users\\\\project\\\\src\\\\file.ts\"),\n ).toBe(\"C:/Users/project/src/file.js\");\n });\n\n it(\"should normalize Windows paths with mixed separators\", () => {\n expect(standardizePathAndFileExtension(\"C:\\\\Users/project\\\\src/file.tsx\"))\n .toBe(\"C:/Users/project/src/file.js\");\n });\n\n it(\"should preserve paths that already use forward slashes\", () => {\n expect(standardizePathAndFileExtension(\"/Users/project/src/file.ts\"))\n .toBe(\"/Users/project/src/file.js\");\n });\n\n it(\"should handle Windows config file paths\", () => {\n expect(\n standardizePathAndFileExtension(\n \"D:\\\\workspace\\\\widgets\\\\my-widget.config\",\n ),\n ).toBe(\"D:/workspace/widgets/my-widget.config.js\");\n });\n\n it(\"should handle Windows paths without file extensions\", () => {\n expect(standardizePathAndFileExtension(\"C:\\\\Users\\\\project\\\\README\"))\n .toBe(\"C:/Users/project/README\");\n });\n });\n\n describe(\"edge cases\", () => {\n it(\"should handle empty string\", () => {\n expect(standardizePathAndFileExtension(\"\")).toBe(\"\");\n });\n\n it(\"should handle paths with multiple dots\", () => {\n expect(standardizePathAndFileExtension(\"/path/to/file.test.ts\")).toBe(\n \"/path/to/file.test.js\",\n );\n });\n\n it(\"should handle Windows paths with multiple dots\", () => {\n expect(standardizePathAndFileExtension(\"C:\\\\path\\\\to\\\\file.test.tsx\"))\n .toBe(\"C:/path/to/file.test.js\");\n });\n\n it(\"should handle virtual paths with @fs prefix\", () => {\n expect(\n standardizePathAndFileExtension(\n \"/@fs/C:\\\\Users\\\\project\\\\src\\\\file.ts\",\n ),\n ).toBe(\"/@fs/C:/Users/project/src/file.js\");\n });\n\n it(\"should handle paths with /src/ directory for Windows\", () => {\n const windowsPath = \"C:\\\\projects\\\\my-app\\\\src\\\\components\\\\Widget.tsx\";\n const result = standardizePathAndFileExtension(windowsPath);\n expect(result).toBe(\"C:/projects/my-app/src/components/Widget.js\");\n expect(result.includes(\"/src/\")).toBe(true);\n });\n });\n});\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -22,10 +22,13 @@ export async function extractWidgetConfig(moduleId, server) {
|
|
|
22
22
|
if (config == null) {
|
|
23
23
|
throw new Error(`No default export found in ${moduleId}`);
|
|
24
24
|
}
|
|
25
|
+
if (typeof config !== "object" || config["id"] == null && config["name"] == null) {
|
|
26
|
+
server.config.logger.warn(`Config object does not look like a widget config: ${JSON.stringify(config)}`);
|
|
27
|
+
}
|
|
25
28
|
validateWidgetConfig(config);
|
|
26
29
|
return config;
|
|
27
30
|
} catch (error) {
|
|
28
|
-
throw new Error(`
|
|
31
|
+
throw new Error(`Encountered error: '${error instanceof Error ? error.message : error}' while loading widget config from ${moduleId}`, {
|
|
29
32
|
cause: error
|
|
30
33
|
});
|
|
31
34
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extractWidgetConfig.js","names":["validateWidgetConfig","extractWidgetConfig","moduleId","server","configModule","ssrLoadModule","config","default","Error","error","cause"],"sources":["extractWidgetConfig.ts"],"sourcesContent":["/*\n * Copyright 2025 Palantir Technologies, Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { ParameterConfig, WidgetConfig } from \"@osdk/widget.api\";\nimport type { ViteDevServer } from \"vite\";\nimport { validateWidgetConfig } from \"./validateWidgetConfig.js\";\n\nexport async function extractWidgetConfig(\n moduleId: string,\n server: ViteDevServer,\n): Promise<WidgetConfig<ParameterConfig>> {\n try {\n const configModule = await server.ssrLoadModule(moduleId);\n const config = configModule.default;\n\n if (config == null) {\n throw new Error(`No default export found in ${moduleId}`);\n }\n\n validateWidgetConfig(config);\n return config as WidgetConfig<ParameterConfig>;\n } catch (error) {\n throw new Error(`
|
|
1
|
+
{"version":3,"file":"extractWidgetConfig.js","names":["validateWidgetConfig","extractWidgetConfig","moduleId","server","configModule","ssrLoadModule","config","default","Error","logger","warn","JSON","stringify","error","message","cause"],"sources":["extractWidgetConfig.ts"],"sourcesContent":["/*\n * Copyright 2025 Palantir Technologies, Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { ParameterConfig, WidgetConfig } from \"@osdk/widget.api\";\nimport type { ViteDevServer } from \"vite\";\nimport { validateWidgetConfig } from \"./validateWidgetConfig.js\";\n\nexport async function extractWidgetConfig(\n moduleId: string,\n server: ViteDevServer,\n): Promise<WidgetConfig<ParameterConfig>> {\n try {\n const configModule = await server.ssrLoadModule(moduleId);\n const config = configModule.default;\n\n if (config == null) {\n throw new Error(`No default export found in ${moduleId}`);\n }\n\n if (\n typeof config !== \"object\"\n || (config[\"id\"] == null && config[\"name\"] == null)\n ) {\n server.config.logger.warn(\n `Config object does not look like a widget config: ${\n JSON.stringify(config)\n }`,\n );\n }\n\n validateWidgetConfig(config);\n return config as WidgetConfig<ParameterConfig>;\n } catch (error) {\n throw new Error(\n `Encountered error: '${(error instanceof Error\n ? error.message\n : error)}' while loading widget config from ${moduleId}`,\n { cause: error },\n );\n }\n}\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAIA,SAASA,oBAAoB,QAAQ,2BAA2B;AAEhE,OAAO,eAAeC,mBAAmBA,CACvCC,QAAgB,EAChBC,MAAqB,EACmB;EACxC,IAAI;IACF,MAAMC,YAAY,GAAG,MAAMD,MAAM,CAACE,aAAa,CAACH,QAAQ,CAAC;IACzD,MAAMI,MAAM,GAAGF,YAAY,CAACG,OAAO;IAEnC,IAAID,MAAM,IAAI,IAAI,EAAE;MAClB,MAAM,IAAIE,KAAK,CAAC,8BAA8BN,QAAQ,EAAE,CAAC;IAC3D;IAEA,IACE,OAAOI,MAAM,KAAK,QAAQ,IACtBA,MAAM,CAAC,IAAI,CAAC,IAAI,IAAI,IAAIA,MAAM,CAAC,MAAM,CAAC,IAAI,IAAK,EACnD;MACAH,MAAM,CAACG,MAAM,CAACG,MAAM,CAACC,IAAI,CACvB,qDACEC,IAAI,CAACC,SAAS,CAACN,MAAM,CAAC,EAE1B,CAAC;IACH;IAEAN,oBAAoB,CAACM,MAAM,CAAC;IAC5B,OAAOA,MAAM;EACf,CAAC,CAAC,OAAOO,KAAK,EAAE;IACd,MAAM,IAAIL,KAAK,CACb,uBAAwBK,KAAK,YAAYL,KAAK,GAC1CK,KAAK,CAACC,OAAO,GACbD,KAAK,sCAAuCX,QAAQ,EAAE,EAC1D;MAAEa,KAAK,EAAEF;IAAM,CACjB,CAAC;EACH;AACF","ignoreList":[]}
|
|
@@ -14,12 +14,21 @@
|
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
+
import { CONFIG_FILE_SUFFIX } from "./constants.js";
|
|
18
|
+
|
|
17
19
|
/**
|
|
18
20
|
* Users may import `.js` files in their code, where the source file is actually a `.ts`, `.tsx`, or
|
|
19
21
|
* `.jsx` file on disk. This standardizes these file extension to `.js` so that we can match imports
|
|
20
|
-
* to source files.
|
|
22
|
+
* to source files. Additionally, extension-less imports may be used depending on the module
|
|
23
|
+
* resolution setting so we append `.js` to config file imports to standardize those as well.
|
|
24
|
+
*
|
|
25
|
+
* Additionally, replaces Windows-style backslashes with forward slashes for path consistency.
|
|
21
26
|
*/
|
|
22
|
-
export function
|
|
23
|
-
|
|
27
|
+
export function standardizePathAndFileExtension(file) {
|
|
28
|
+
const normalizedPath = file.replace(/\\/g, "/");
|
|
29
|
+
if (normalizedPath.endsWith(CONFIG_FILE_SUFFIX)) {
|
|
30
|
+
return normalizedPath + ".js";
|
|
31
|
+
}
|
|
32
|
+
return normalizedPath.replace(/\.[jt]sx?$/, ".js");
|
|
24
33
|
}
|
|
25
|
-
//# sourceMappingURL=
|
|
34
|
+
//# sourceMappingURL=standardizePathAndFileExtension.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"standardizePathAndFileExtension.js","names":["CONFIG_FILE_SUFFIX","standardizePathAndFileExtension","file","normalizedPath","replace","endsWith"],"sources":["standardizePathAndFileExtension.ts"],"sourcesContent":["/*\n * Copyright 2025 Palantir Technologies, Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { CONFIG_FILE_SUFFIX } from \"./constants.js\";\n\n/**\n * Users may import `.js` files in their code, where the source file is actually a `.ts`, `.tsx`, or\n * `.jsx` file on disk. This standardizes these file extension to `.js` so that we can match imports\n * to source files. Additionally, extension-less imports may be used depending on the module\n * resolution setting so we append `.js` to config file imports to standardize those as well.\n *\n * Additionally, replaces Windows-style backslashes with forward slashes for path consistency.\n */\nexport function standardizePathAndFileExtension(file: string): string {\n const normalizedPath = file.replace(/\\\\/g, \"/\");\n if (normalizedPath.endsWith(CONFIG_FILE_SUFFIX)) {\n return normalizedPath + \".js\";\n }\n return normalizedPath.replace(/\\.[jt]sx?$/, \".js\");\n}\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,SAASA,kBAAkB,QAAQ,gBAAgB;;AAEnD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,+BAA+BA,CAACC,IAAY,EAAU;EACpE,MAAMC,cAAc,GAAGD,IAAI,CAACE,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;EAC/C,IAAID,cAAc,CAACE,QAAQ,CAACL,kBAAkB,CAAC,EAAE;IAC/C,OAAOG,cAAc,GAAG,KAAK;EAC/B;EACA,OAAOA,cAAc,CAACC,OAAO,CAAC,YAAY,EAAE,KAAK,CAAC;AACpD","ignoreList":[]}
|
|
@@ -20,12 +20,14 @@ import color from "picocolors";
|
|
|
20
20
|
import sirv from "sirv";
|
|
21
21
|
import { CONFIG_FILE_SUFFIX, DEV_PLUGIN_ID, ENTRYPOINTS_PATH, FINISH_PATH, MODULE_EVALUATION_MODE, SETUP_PATH, VITE_INJECTIONS_PATH } from "../common/constants.js";
|
|
22
22
|
import { getInputHtmlEntrypoints } from "../common/getInputHtmlEntrypoints.js";
|
|
23
|
-
import {
|
|
23
|
+
import { standardizePathAndFileExtension } from "../common/standardizePathAndFileExtension.js";
|
|
24
|
+
import { isCodeWorkspacesMode } from "./codeWorkspacesMode.js";
|
|
24
25
|
import { extractInjectedScripts } from "./extractInjectedScripts.js";
|
|
25
26
|
import { getBaseHref } from "./getBaseHref.js";
|
|
26
27
|
import { getFoundryToken } from "./getFoundryToken.js";
|
|
27
28
|
import { getWidgetIdOverrideMap } from "./getWidgetIdOverrideMap.js";
|
|
28
29
|
import { publishDevModeSettings } from "./publishDevModeSettings.js";
|
|
30
|
+
import { warnIfWrongDevCommand } from "./validateDevEnvironment.js";
|
|
29
31
|
|
|
30
32
|
// Location of the setup page assets
|
|
31
33
|
const DIR_DIST = typeof __dirname !== "undefined" ? __dirname : path.dirname(fileURLToPath(import.meta.url));
|
|
@@ -58,7 +60,8 @@ export function FoundryWidgetDevPlugin() {
|
|
|
58
60
|
/**
|
|
59
61
|
* Check for the required token environment variable in dev mode.
|
|
60
62
|
*/
|
|
61
|
-
|
|
63
|
+
configResolved(resolvedConfig) {
|
|
64
|
+
warnIfWrongDevCommand(resolvedConfig.mode, resolvedConfig.logger);
|
|
62
65
|
getFoundryToken(resolvedConfig.mode);
|
|
63
66
|
},
|
|
64
67
|
/**
|
|
@@ -105,11 +108,30 @@ export function FoundryWidgetDevPlugin() {
|
|
|
105
108
|
/**
|
|
106
109
|
* Finish the setup process by setting the widget overrides in Foundry and enabling dev mode.
|
|
107
110
|
*/
|
|
108
|
-
server.middlewares.use(serverPath(server, FINISH_PATH), async (
|
|
111
|
+
server.middlewares.use(serverPath(server, FINISH_PATH), async (req, res) => {
|
|
112
|
+
// Check for too many attempts
|
|
113
|
+
if (req.url == null) {
|
|
114
|
+
throw new Error("Request URL is undefined");
|
|
115
|
+
}
|
|
116
|
+
const url = new URL(req.url, `http://${req.headers.host}`);
|
|
117
|
+
const numAttempts = parseInt(url.searchParams.get("attempt") ?? "0");
|
|
118
|
+
if (numAttempts >= 10) {
|
|
119
|
+
const errorMessage = "Timed out waiting for widget config files to be parsed. Are you sure a widget config is imported?";
|
|
120
|
+
server.config.logger.error(errorMessage);
|
|
121
|
+
res.setHeader("Content-Type", "application/json");
|
|
122
|
+
res.statusCode = 500;
|
|
123
|
+
res.end(JSON.stringify({
|
|
124
|
+
status: "error",
|
|
125
|
+
error: errorMessage
|
|
126
|
+
}));
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
109
130
|
// Wait for the setup page to trigger the parsing of the config files
|
|
110
131
|
const numEntrypoints = htmlEntrypoints.length;
|
|
111
132
|
const numConfigFiles = Object.keys(configFileToEntrypoint).length;
|
|
112
133
|
if (numConfigFiles < numEntrypoints) {
|
|
134
|
+
server.config.logger.info(`Waiting for widget config files to be parsed, found ${numConfigFiles} config files out of` + ` ${numEntrypoints} HTML entrypoints.`);
|
|
113
135
|
res.setHeader("Content-Type", "application/json");
|
|
114
136
|
res.end(JSON.stringify({
|
|
115
137
|
status: "pending"
|
|
@@ -144,23 +166,27 @@ export function FoundryWidgetDevPlugin() {
|
|
|
144
166
|
}
|
|
145
167
|
|
|
146
168
|
// Standardize the source file extension and get the full path
|
|
147
|
-
const standardizedSource =
|
|
169
|
+
const standardizedSource = standardizePathAndFileExtension(getFullSourcePath(
|
|
148
170
|
// If the source path is absolute, resolve it against the current working directory
|
|
149
171
|
source.startsWith("/") ? path.join(process.cwd(), source) : source, importer));
|
|
150
172
|
// Importers are already full paths, so just standardize the extension
|
|
151
|
-
|
|
152
|
-
const standardizedImporter = standardizeFileExtension(path.normalize(importer));
|
|
173
|
+
const standardizedImporter = standardizePathAndFileExtension(importer);
|
|
153
174
|
|
|
154
175
|
// In dev mode all entrypoints have a generic HTML importer value
|
|
155
|
-
if (importer.endsWith("index.html") && !standardizedSource.includes("@fs")
|
|
176
|
+
if (importer.endsWith("index.html") && !standardizedSource.includes("@fs")
|
|
177
|
+
// In a cold start, Vite may try to resolve files (e.g. a widget.html) before the user even accesses the dev mode server.
|
|
178
|
+
// These files are not valid code entrypoints, so we ignore them here.
|
|
179
|
+
&& path.extname(standardizedSource) !== ".html") {
|
|
156
180
|
// Store the fully resolved path and the relative path, as we need the former for mapping
|
|
157
181
|
// config files to entrypoints and the latter as a dev mode override script
|
|
158
182
|
codeEntrypoints[standardizedSource] = source;
|
|
159
183
|
}
|
|
160
184
|
|
|
161
185
|
// Look for config files that are imported from an entrypoint file
|
|
162
|
-
|
|
163
|
-
|
|
186
|
+
// Also check the config file being imported is in src to avoid picking up imports for other
|
|
187
|
+
// project files like foundry.config.json / eslint.config.mjs when tailwind is used.
|
|
188
|
+
if (standardizedSource.replace(/\.[^/.]+$/, "").endsWith(CONFIG_FILE_SUFFIX) && standardizedSource.includes("/src/") && codeEntrypoints[standardizedImporter] != null) {
|
|
189
|
+
const fullSourcePath = standardizePathAndFileExtension(getFullSourcePath(source, standardizedImporter));
|
|
164
190
|
configFileToEntrypoint[fullSourcePath] = standardizedImporter;
|
|
165
191
|
}
|
|
166
192
|
}
|
|
@@ -178,7 +204,11 @@ function serverPath(server, subPath) {
|
|
|
178
204
|
return path.posix.resolve(server.config.base, subPath);
|
|
179
205
|
}
|
|
180
206
|
function printSetupPageUrl(server) {
|
|
181
|
-
|
|
182
|
-
|
|
207
|
+
if (isCodeWorkspacesMode(server.config.mode)) {
|
|
208
|
+
server.config.logger.info(` ${color.green("➜")} ${color.bold("Select a widget from the preview panel to enter developer mode")}`);
|
|
209
|
+
} else {
|
|
210
|
+
const setupRoute = `${getBaseHref(server)}${SETUP_PATH}/`;
|
|
211
|
+
server.config.logger.info(` ${color.green("➜")} ${color.bold("Click to enter developer mode for your widget set")}: ${color.green(setupRoute)}`);
|
|
212
|
+
}
|
|
183
213
|
}
|
|
184
214
|
//# sourceMappingURL=FoundryWidgetDevPlugin.js.map
|