{"record":{"id":"c38cb4ddde2b94b5","repo":"halo-dev/halo","slug":"esm-ui-provider-output-must-expose-a-default-plugi","errorCode":null,"errorMessage":"ESM UI provider output must expose a default PluginModule export.","messagePattern":"ESM UI provider output must expose a default PluginModule export\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"ui/packages/ui-plugin-bundler-kit/src/rsbuild-esm.ts","lineNumber":69,"sourceCode":"          if (entryScripts.length !== 1) {\n            throw new Error(\n              \"ESM UI provider output must contain exactly one entry JavaScript file.\"\n            );\n          }\n          const entryFile = entryScripts[0];\n          const entry = assets[entryFile];\n          if (!entry) {\n            throw new Error(\n              `ESM UI provider output is missing its entry asset ${entryFile}.`\n            );\n          }\n          const entryCode = entry.source().toString();\n          await validator.validateSource(entryCode, entryFile);\n          if (\n            !/\\bexport\\s+default\\b/.test(entryCode) &&\n            !/\\bexport\\s*\\{[^}]*\\bdefault\\b[^}]*\\}/s.test(entryCode)\n          ) {\n            throw new Error(\n              \"ESM UI provider output must expose a default PluginModule export.\"\n            );\n          }\n          const entryStyles =\n            compilation.entrypoints\n              .get(\"main\")\n              ?.getFiles()\n              .filter((fileName) => fileName.endsWith(\".css\")) || [];\n          if (entryStyles.length > 1) {\n            throw new Error(\n              \"ESM UI provider output must contain at most one entry stylesheet.\"\n            );\n          }\n          const manifest = validateEsmProviderManifest({\n            format: \"esm\",\n            entry: `./${entryFile}`,\n            ...(entryStyles[0] ? { style: `./${entryStyles[0]}` } : {}),\n          });","sourceCodeStart":51,"sourceCodeEnd":87,"githubUrl":"https://github.com/halo-dev/halo/blob/d2f5165f9c8f055ffcb3fa9c3f4032821a7b68c8/ui/packages/ui-plugin-bundler-kit/src/rsbuild-esm.ts#L51-L87","documentation":"normalizeResourcePath rejects resource paths that are not provider-root-relative: a leading slash (absolute), a leading //, a '?' query, a '#' fragment, or a scheme (matched by ^[a-zA-Z][a-zA-Z0-9+.-]*:.*). The offending raw path is included in the message; the exception becomes ClassifiedProvider.invalid upstream.","triggerScenarios":"ui-plugin.json's entry/style is absolute (\"/index.js\"), protocol-prefixed (\"https://cdn/x.js\"), or carries query/fragment markers.","commonSituations":"Author copies a full URL or absolute path expecting it to load externally; bundler emits a leading slash; CDN reference pasted by mistake.","solutions":["Use a relative path with no leading slash, scheme, ?, or # — e.g. \"index.js\".","Vendor any remote asset into the plugin's console/ directory and reference it locally.","Remove leading slashes produced by the bundler's publicPath setting."],"exampleFix":"// before\n{\n  \"format\": \"esm\",\n  \"entry\": \"/assets/index.js\"\n}\n// after\n{\n  \"format\": \"esm\",\n  \"entry\": \"index.js\"\n}","handlingStrategy":"validation","validationCode":"String slashed = entry.replace('\\\\', '/');\nif (slashed.startsWith(\"/\") || slashed.contains(\"?\") || slashed.contains(\"#\")\n        || slashed.matches(\"^[a-zA-Z][a-zA-Z0-9+.-]*:.*\")) {\n    throw new IllegalStateException(\"entry must be provider-root-relative: \" + entry);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use relative paths only — no leading slash, scheme, query, or fragment.","Vendor remote assets locally instead of URL-referencing them.","Configure the bundler's publicPath to '' so it emits relative paths."],"tags":["plugin","ui-bundle","esm","provider-manifest","path-traversal","security"],"backgroundTag":null,"analyzedSha":"d2f5165f9c8f055ffcb3fa9c3f4032821a7b68c8","analyzedAt":"2026-08-14T00:18:38.915Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}