veryfront 0.1.83 → 0.1.85
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/esm/cli/commands/knowledge/command.d.ts +10 -13
- package/esm/cli/commands/knowledge/command.d.ts.map +1 -1
- package/esm/cli/commands/knowledge/command.js +294 -110
- package/esm/cli/commands/knowledge/parser-source.d.ts.map +1 -1
- package/esm/cli/commands/knowledge/parser-source.js +52 -0
- package/esm/cli/commands/knowledge/result.d.ts +54 -0
- package/esm/cli/commands/knowledge/result.d.ts.map +1 -0
- package/esm/cli/commands/knowledge/result.js +22 -0
- package/esm/cli/commands/knowledge/source-policy.d.ts +11 -0
- package/esm/cli/commands/knowledge/source-policy.d.ts.map +1 -0
- package/esm/cli/commands/knowledge/source-policy.js +135 -0
- package/esm/deno.js +1 -1
- package/esm/src/jobs/index.d.ts +1 -1
- package/esm/src/jobs/index.d.ts.map +1 -1
- package/esm/src/jobs/index.js +1 -1
- package/esm/src/jobs/schemas.d.ts +1104 -185
- package/esm/src/jobs/schemas.d.ts.map +1 -1
- package/esm/src/jobs/schemas.js +81 -8
- package/esm/src/proxy/error-response.d.ts +7 -0
- package/esm/src/proxy/error-response.d.ts.map +1 -0
- package/esm/src/proxy/error-response.js +26 -0
- package/esm/src/proxy/handler.d.ts.map +1 -1
- package/esm/src/proxy/handler.js +25 -0
- package/esm/src/proxy/main.js +2 -23
- package/esm/src/rendering/orchestrator/css-candidate-manifest.d.ts +11 -0
- package/esm/src/rendering/orchestrator/css-candidate-manifest.d.ts.map +1 -1
- package/esm/src/rendering/orchestrator/css-candidate-manifest.js +23 -12
- package/esm/src/server/handlers/dev/styles-candidate-scanner.d.ts.map +1 -1
- package/esm/src/server/handlers/dev/styles-candidate-scanner.js +25 -8
- package/esm/src/server/handlers/dev/styles-css.handler.d.ts +1 -0
- package/esm/src/server/handlers/dev/styles-css.handler.d.ts.map +1 -1
- package/esm/src/server/handlers/dev/styles-css.handler.js +15 -5
- package/esm/src/transforms/esm/http-cache-helpers.d.ts.map +1 -1
- package/esm/src/transforms/esm/http-cache-helpers.js +7 -1
- package/esm/src/transforms/import-rewriter/strategies/bare-strategy.d.ts.map +1 -1
- package/esm/src/transforms/import-rewriter/strategies/bare-strategy.js +11 -8
- package/esm/src/transforms/shared/package-specifier.d.ts +7 -0
- package/esm/src/transforms/shared/package-specifier.d.ts.map +1 -0
- package/esm/src/transforms/shared/package-specifier.js +19 -0
- package/package.json +1 -1
- package/src/cli/commands/knowledge/command.ts +375 -139
- package/src/cli/commands/knowledge/parser-source.ts +52 -0
- package/src/cli/commands/knowledge/result.ts +88 -0
- package/src/cli/commands/knowledge/source-policy.ts +164 -0
- package/src/deno.js +1 -1
- package/src/src/jobs/index.ts +16 -0
- package/src/src/jobs/schemas.ts +105 -8
- package/src/src/proxy/error-response.ts +33 -0
- package/src/src/proxy/handler.ts +43 -0
- package/src/src/proxy/main.ts +2 -27
- package/src/src/rendering/orchestrator/css-candidate-manifest.ts +40 -14
- package/src/src/server/handlers/dev/styles-candidate-scanner.ts +37 -11
- package/src/src/server/handlers/dev/styles-css.handler.ts +25 -4
- package/src/src/transforms/esm/http-cache-helpers.ts +12 -1
- package/src/src/transforms/import-rewriter/strategies/bare-strategy.ts +11 -12
- package/src/src/transforms/shared/package-specifier.ts +29 -0
package/src/src/proxy/main.ts
CHANGED
|
@@ -37,12 +37,12 @@ import {
|
|
|
37
37
|
withSpan,
|
|
38
38
|
} from "./tracing.js";
|
|
39
39
|
import { proxyLogger, runWithProxyRequestContext } from "./logger.js";
|
|
40
|
-
import { ErrorPages } from "../server/utils/error-html.js";
|
|
41
40
|
import { RendererRouter } from "./renderer-router.js";
|
|
42
41
|
import { ServerResolver } from "./server-resolver.js";
|
|
43
42
|
import { parseProjectDomain } from "../server/utils/domain-parser.js";
|
|
44
43
|
import { exit, getEnv, onSignal } from "../platform/compat/process.js";
|
|
45
44
|
import { createHttpServer, upgradeWebSocket } from "../platform/compat/http/index.js";
|
|
45
|
+
import { createProxyErrorResponse, jsonErrorResponse } from "./error-response.js";
|
|
46
46
|
|
|
47
47
|
function getLocalProjects(): Record<string, string> {
|
|
48
48
|
const raw = getEnv("LOCAL_PROJECTS");
|
|
@@ -274,13 +274,6 @@ function handleWebSocketUpgrade(req: dntShim.Request): dntShim.Response {
|
|
|
274
274
|
return response;
|
|
275
275
|
}
|
|
276
276
|
|
|
277
|
-
function jsonErrorResponse(status: number, body: Record<string, unknown>): dntShim.Response {
|
|
278
|
-
return new dntShim.Response(JSON.stringify(body), {
|
|
279
|
-
status,
|
|
280
|
-
headers: { "Content-Type": "application/json" },
|
|
281
|
-
});
|
|
282
|
-
}
|
|
283
|
-
|
|
284
277
|
function forwardToServer(req: dntShim.Request): Promise<dntShim.Response> {
|
|
285
278
|
const startTime = performance.now();
|
|
286
279
|
const url = new URL(req.url);
|
|
@@ -311,25 +304,7 @@ function forwardToServer(req: dntShim.Request): Promise<dntShim.Response> {
|
|
|
311
304
|
const logLevel = ctx.error.status < 500 ? "warn" : "error";
|
|
312
305
|
proxyLogger[logLevel](`${ctx.error.status} ${req.method} ${url.pathname}`, { ms });
|
|
313
306
|
endSpan(spanInfo?.span, ctx.error.status);
|
|
314
|
-
|
|
315
|
-
if (ctx.error.redirectUrl) {
|
|
316
|
-
return new dntShim.Response(null, {
|
|
317
|
-
status: 302,
|
|
318
|
-
headers: { Location: ctx.error.redirectUrl },
|
|
319
|
-
});
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
if (ctx.error.slug === "release-not-found") {
|
|
323
|
-
return new dntShim.Response(ErrorPages.notFound(), {
|
|
324
|
-
status: 404,
|
|
325
|
-
headers: { "Content-Type": "text/html; charset=utf-8" },
|
|
326
|
-
});
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
return jsonErrorResponse(ctx.error.status, {
|
|
330
|
-
error: ctx.error.message,
|
|
331
|
-
status: ctx.error.status,
|
|
332
|
-
});
|
|
307
|
+
return createProxyErrorResponse(ctx.error);
|
|
333
308
|
}
|
|
334
309
|
|
|
335
310
|
const reqLogger = proxyLogger.child({
|
|
@@ -23,6 +23,14 @@ interface RouteCandidateOptions {
|
|
|
23
23
|
developmentMode: boolean;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
interface ProjectCandidateOptions {
|
|
27
|
+
projectScope: string;
|
|
28
|
+
projectVersion: string;
|
|
29
|
+
projectDir: string;
|
|
30
|
+
files: SourceFileLike[];
|
|
31
|
+
developmentMode: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
26
34
|
const logger = rendererLogger.component("css-candidate-manifest");
|
|
27
35
|
const SOURCE_EXTENSIONS = [".tsx", ".jsx", ".mdx", ".ts", ".js"];
|
|
28
36
|
const DEV_MANIFEST_TTL_MS = 2_000;
|
|
@@ -90,6 +98,29 @@ function buildCandidateManifest(files: SourceFileLike[], projectDir: string): Ca
|
|
|
90
98
|
return { fileCandidates, allCandidates, builtAt: Date.now() };
|
|
91
99
|
}
|
|
92
100
|
|
|
101
|
+
function getOrBuildManifest(
|
|
102
|
+
options: Pick<
|
|
103
|
+
ProjectCandidateOptions,
|
|
104
|
+
"projectScope" | "projectVersion" | "projectDir" | "files" | "developmentMode"
|
|
105
|
+
>,
|
|
106
|
+
): CandidateManifest {
|
|
107
|
+
const manifestKey = buildManifestCacheKey(options.projectScope, options.projectVersion);
|
|
108
|
+
const existingManifest = manifestCache.get(manifestKey);
|
|
109
|
+
const manifest = shouldRebuildManifest(existingManifest, options.developmentMode)
|
|
110
|
+
? buildCandidateManifest(options.files, options.projectDir)
|
|
111
|
+
: existingManifest!;
|
|
112
|
+
|
|
113
|
+
if (manifest !== existingManifest) {
|
|
114
|
+
manifestCache.set(manifestKey, manifest);
|
|
115
|
+
|
|
116
|
+
for (const key of routeCandidateCache.keys()) {
|
|
117
|
+
if (key.startsWith(`${manifestKey}:`)) routeCandidateCache.delete(key);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return manifest;
|
|
122
|
+
}
|
|
123
|
+
|
|
93
124
|
function addCandidatesForPath(
|
|
94
125
|
target: Set<string>,
|
|
95
126
|
manifest: CandidateManifest,
|
|
@@ -109,20 +140,7 @@ function addCandidatesForPath(
|
|
|
109
140
|
*/
|
|
110
141
|
export function getRouteCandidates(options: RouteCandidateOptions): Set<string> {
|
|
111
142
|
const manifestKey = buildManifestCacheKey(options.projectScope, options.projectVersion);
|
|
112
|
-
const
|
|
113
|
-
const manifest = shouldRebuildManifest(existingManifest, options.developmentMode)
|
|
114
|
-
? buildCandidateManifest(options.files, options.projectDir)
|
|
115
|
-
: existingManifest!;
|
|
116
|
-
|
|
117
|
-
if (manifest !== existingManifest) {
|
|
118
|
-
manifestCache.set(manifestKey, manifest);
|
|
119
|
-
|
|
120
|
-
// Clear route subsets when project-level file manifest is rebuilt.
|
|
121
|
-
for (const key of routeCandidateCache.keys()) {
|
|
122
|
-
if (key.startsWith(`${manifestKey}:`)) routeCandidateCache.delete(key);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
143
|
+
const manifest = getOrBuildManifest(options);
|
|
126
144
|
const routeCacheKey = `${manifestKey}:${options.routeKey}`;
|
|
127
145
|
const cachedRoute = routeCandidateCache.get(routeCacheKey);
|
|
128
146
|
if (cachedRoute) return new Set(cachedRoute);
|
|
@@ -156,6 +174,14 @@ export function getRouteCandidates(options: RouteCandidateOptions): Set<string>
|
|
|
156
174
|
return new Set(routeCandidates);
|
|
157
175
|
}
|
|
158
176
|
|
|
177
|
+
/**
|
|
178
|
+
* Resolve full-project Tailwind candidates from a precomputed per-project manifest.
|
|
179
|
+
*/
|
|
180
|
+
export function getProjectCandidates(options: ProjectCandidateOptions): Set<string> {
|
|
181
|
+
const manifest = getOrBuildManifest(options);
|
|
182
|
+
return new Set(manifest.allCandidates);
|
|
183
|
+
}
|
|
184
|
+
|
|
159
185
|
/**
|
|
160
186
|
* Invalidate cached candidate manifests for one project scope (or all scopes).
|
|
161
187
|
*/
|
|
@@ -12,6 +12,8 @@ import { extractCandidates } from "../../../html/styles-builder/tailwind-compile
|
|
|
12
12
|
import { serverLogger } from "../../../utils/index.js";
|
|
13
13
|
import { createFileSystem } from "../../../platform/compat/fs.js";
|
|
14
14
|
import { join } from "../../../platform/compat/path/index.js";
|
|
15
|
+
import { getProjectCandidates } from "../../../rendering/orchestrator/css-candidate-manifest.js";
|
|
16
|
+
import type { ResolvedContentContext } from "../../../platform/adapters/fs/veryfront/types.js";
|
|
15
17
|
import type { HandlerContext } from "../types.js";
|
|
16
18
|
import { FRAMEWORK_CANDIDATES } from "./framework-candidates.generated.js";
|
|
17
19
|
|
|
@@ -23,6 +25,25 @@ const SKIP_DIRS = new Set(["node_modules", ".cache", ".git", "dist", "build", ".
|
|
|
23
25
|
/** De-duplicated set of framework candidates, computed once at import time. */
|
|
24
26
|
const frameworkCandidates = new Set<string>(FRAMEWORK_CANDIDATES);
|
|
25
27
|
|
|
28
|
+
interface SourceFileProvider {
|
|
29
|
+
getAllSourceFiles?: () =>
|
|
30
|
+
| Array<{ path: string; content?: string }>
|
|
31
|
+
| Promise<Array<{ path: string; content?: string }>>;
|
|
32
|
+
getContentContext?: () => ResolvedContentContext | null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function resolveProjectVersion(
|
|
36
|
+
ctx: HandlerContext,
|
|
37
|
+
contentContext: ResolvedContentContext | null,
|
|
38
|
+
): string {
|
|
39
|
+
if (contentContext?.releaseId) return `release:${contentContext.releaseId}`;
|
|
40
|
+
if (contentContext?.branch) return `branch:${contentContext.branch}`;
|
|
41
|
+
if (contentContext?.environmentName) return `environment:${contentContext.environmentName}`;
|
|
42
|
+
if (ctx.releaseId) return `release:${ctx.releaseId}`;
|
|
43
|
+
if (ctx.parsedDomain?.branch) return `branch:${ctx.parsedDomain.branch}`;
|
|
44
|
+
return "live";
|
|
45
|
+
}
|
|
46
|
+
|
|
26
47
|
/**
|
|
27
48
|
* Extract Tailwind CSS candidate class names from all project source files.
|
|
28
49
|
*
|
|
@@ -42,9 +63,8 @@ export async function extractProjectCandidates(ctx: HandlerContext): Promise<Set
|
|
|
42
63
|
|
|
43
64
|
// Call method directly on wrappedFs to preserve 'this' context
|
|
44
65
|
const fsAdapter = wrappedFs.getUnderlyingAdapter() as {
|
|
45
|
-
getAllSourceFiles?:
|
|
46
|
-
|
|
47
|
-
| Promise<Array<{ path: string; content?: string }>>;
|
|
66
|
+
getAllSourceFiles?: SourceFileProvider["getAllSourceFiles"];
|
|
67
|
+
getContentContext?: SourceFileProvider["getContentContext"];
|
|
48
68
|
};
|
|
49
69
|
|
|
50
70
|
if (typeof fsAdapter.getAllSourceFiles !== "function") {
|
|
@@ -56,14 +76,20 @@ export async function extractProjectCandidates(ctx: HandlerContext): Promise<Set
|
|
|
56
76
|
|
|
57
77
|
const candidates = new Set<string>(frameworkCandidates);
|
|
58
78
|
const files = await fsAdapter.getAllSourceFiles();
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
79
|
+
const contentContext = typeof fsAdapter.getContentContext === "function"
|
|
80
|
+
? fsAdapter.getContentContext()
|
|
81
|
+
: null;
|
|
82
|
+
|
|
83
|
+
for (
|
|
84
|
+
const cls of getProjectCandidates({
|
|
85
|
+
projectScope: ctx.projectSlug ?? contentContext?.projectSlug ?? ctx.projectDir,
|
|
86
|
+
projectVersion: resolveProjectVersion(ctx, contentContext),
|
|
87
|
+
projectDir: ctx.projectDir,
|
|
88
|
+
files,
|
|
89
|
+
developmentMode: contentContext?.sourceType === "branch",
|
|
90
|
+
})
|
|
91
|
+
) {
|
|
92
|
+
candidates.add(cls);
|
|
67
93
|
}
|
|
68
94
|
|
|
69
95
|
return candidates;
|
|
@@ -14,6 +14,7 @@ import { joinPath } from "../../../utils/path-utils.js";
|
|
|
14
14
|
import {
|
|
15
15
|
formatCSSError,
|
|
16
16
|
generateTailwindCSS,
|
|
17
|
+
getProjectCSS,
|
|
17
18
|
} from "../../../html/styles-builder/tailwind-compiler.js";
|
|
18
19
|
import { DEFAULT_STYLESHEET } from "../../../html/styles-builder/css-hash-cache.js";
|
|
19
20
|
import { serverLogger } from "../../../utils/index.js";
|
|
@@ -21,6 +22,10 @@ import { extractProjectCandidates } from "./styles-candidate-scanner.js";
|
|
|
21
22
|
|
|
22
23
|
const logger = serverLogger.component("styles-css-handler");
|
|
23
24
|
|
|
25
|
+
type GeneratedStylesResult =
|
|
26
|
+
| Awaited<ReturnType<typeof generateTailwindCSS>>
|
|
27
|
+
| Awaited<ReturnType<typeof getProjectCSS>>;
|
|
28
|
+
|
|
24
29
|
export class StylesCSSHandler extends BaseHandler {
|
|
25
30
|
metadata: HandlerMetadata = {
|
|
26
31
|
name: "StylesCSSHandler",
|
|
@@ -54,11 +59,9 @@ export class StylesCSSHandler extends BaseHandler {
|
|
|
54
59
|
});
|
|
55
60
|
candidates = new Set<string>();
|
|
56
61
|
}
|
|
57
|
-
const result = await
|
|
58
|
-
projectSlug: ctx.projectSlug,
|
|
59
|
-
});
|
|
62
|
+
const result = await this.generateStylesheet(ctx, rawCss, candidates);
|
|
60
63
|
|
|
61
|
-
if (result.error) {
|
|
64
|
+
if ("error" in result && result.error) {
|
|
62
65
|
const formatted = formatCSSError(result.error);
|
|
63
66
|
logger.error("Tailwind error", {
|
|
64
67
|
error: formatted.message,
|
|
@@ -104,6 +107,8 @@ body::before {
|
|
|
104
107
|
logger.debug("CSS generated", {
|
|
105
108
|
candidates: candidates.size,
|
|
106
109
|
cssLength: result.css.length,
|
|
110
|
+
fromCache: "fromCache" in result ? result.fromCache : false,
|
|
111
|
+
cssHash: "hash" in result ? result.hash : undefined,
|
|
107
112
|
});
|
|
108
113
|
|
|
109
114
|
return this.respond(
|
|
@@ -144,4 +149,20 @@ body::before {
|
|
|
144
149
|
return DEFAULT_STYLESHEET;
|
|
145
150
|
}
|
|
146
151
|
}
|
|
152
|
+
|
|
153
|
+
private generateStylesheet(
|
|
154
|
+
ctx: HandlerContext,
|
|
155
|
+
rawCss: string,
|
|
156
|
+
candidates: Set<string>,
|
|
157
|
+
): Promise<GeneratedStylesResult> {
|
|
158
|
+
if (!ctx.projectSlug) {
|
|
159
|
+
return generateTailwindCSS(rawCss, candidates);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return getProjectCSS(ctx.projectSlug, rawCss, candidates, {
|
|
163
|
+
minify: true,
|
|
164
|
+
environment: "preview",
|
|
165
|
+
buildMode: "production",
|
|
166
|
+
});
|
|
167
|
+
}
|
|
147
168
|
}
|
|
@@ -9,6 +9,8 @@ import { cwd } from "../../platform/compat/process.js";
|
|
|
9
9
|
import { rendererLogger } from "../../utils/index.js";
|
|
10
10
|
import { resolveImport } from "../../modules/import-map/resolver.js";
|
|
11
11
|
import type { ImportMapConfig } from "../../modules/import-map/types.js";
|
|
12
|
+
import { buildEsmShUrl } from "../import-rewriter/url-builder.js";
|
|
13
|
+
import { parseBarePackageSpecifier } from "../shared/package-specifier.js";
|
|
12
14
|
import { DEFAULT_REACT_VERSION, getReactImportMap } from "./package-registry.js";
|
|
13
15
|
|
|
14
16
|
const logger = rendererLogger.component("http-cache");
|
|
@@ -149,7 +151,16 @@ export function resolveBareSpecifier(
|
|
|
149
151
|
const mapped = resolveImport(specifier, importMap);
|
|
150
152
|
if (mapped !== specifier) return mapped;
|
|
151
153
|
|
|
152
|
-
|
|
154
|
+
const parsed = parseBarePackageSpecifier(specifier);
|
|
155
|
+
if (parsed == null) {
|
|
156
|
+
return `https://esm.sh/${specifier}?target=es2022`;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return buildEsmShUrl(
|
|
160
|
+
parsed.packageName,
|
|
161
|
+
parsed.version ?? undefined,
|
|
162
|
+
parsed.subpath ?? undefined,
|
|
163
|
+
);
|
|
153
164
|
}
|
|
154
165
|
|
|
155
166
|
/**
|
|
@@ -13,6 +13,7 @@ import type {
|
|
|
13
13
|
RewriteResult,
|
|
14
14
|
} from "../types.js";
|
|
15
15
|
import { buildEsmShUrl, TAILWIND_VERSION } from "../url-builder.js";
|
|
16
|
+
import { parseBarePackageSpecifier } from "../../shared/package-specifier.js";
|
|
16
17
|
|
|
17
18
|
const logger = rendererLogger.component("esm");
|
|
18
19
|
|
|
@@ -22,10 +23,6 @@ function hasVersionSpecifier(specifier: string): boolean {
|
|
|
22
23
|
return /@[\d^~x][\d.x^~-]*(?=\/|$)/.test(specifier);
|
|
23
24
|
}
|
|
24
25
|
|
|
25
|
-
function normalizeVersionedSpecifier(specifier: string): string {
|
|
26
|
-
return specifier.replace(/@[\d^~x][\d.x^~-]*(?=\/|$)/, "");
|
|
27
|
-
}
|
|
28
|
-
|
|
29
26
|
function warnUnversionedImport(specifier: string, projectId: string): void {
|
|
30
27
|
const key = `${projectId}:${specifier}`;
|
|
31
28
|
if (unversionedImportsWarned.has(key)) return;
|
|
@@ -73,20 +70,22 @@ export class BareStrategy implements ImportRewriteStrategy {
|
|
|
73
70
|
rewrite(info: ImportSpecifierInfo, ctx: RewriteContext): RewriteResult {
|
|
74
71
|
if (ctx.target === "ssr") return { specifier: null };
|
|
75
72
|
|
|
76
|
-
const
|
|
73
|
+
const parsed = parseBarePackageSpecifier(info.specifier);
|
|
74
|
+
if (parsed == null) {
|
|
75
|
+
return { specifier: null };
|
|
76
|
+
}
|
|
77
77
|
|
|
78
|
-
|
|
78
|
+
const packageName = parsed.packageName;
|
|
79
|
+
let version = parsed.version ?? undefined;
|
|
80
|
+
const subpath = parsed.subpath ?? undefined;
|
|
79
81
|
|
|
80
|
-
if (
|
|
81
|
-
|
|
82
|
-
/^tailwindcss/,
|
|
83
|
-
`tailwindcss@${TAILWIND_VERSION}`,
|
|
84
|
-
);
|
|
82
|
+
if (packageName === "tailwindcss") {
|
|
83
|
+
version = TAILWIND_VERSION;
|
|
85
84
|
} else if (!hasVersionSpecifier(info.specifier)) {
|
|
86
85
|
warnUnversionedImport(info.specifier, ctx.projectId);
|
|
87
86
|
}
|
|
88
87
|
|
|
89
|
-
const url = buildEsmShUrl(
|
|
88
|
+
const url = buildEsmShUrl(packageName, version, subpath, {
|
|
90
89
|
external: ["react"],
|
|
91
90
|
});
|
|
92
91
|
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export interface ParsedBarePackageSpecifier {
|
|
2
|
+
packageName: string;
|
|
3
|
+
version: string | null;
|
|
4
|
+
subpath: string | null;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function parseBarePackageSpecifier(
|
|
8
|
+
specifier: string,
|
|
9
|
+
): ParsedBarePackageSpecifier | null {
|
|
10
|
+
const scopedMatch = specifier.match(/^(@[^/]+\/[^/@]+)(?:@([^/]+))?(\/.*)?$/);
|
|
11
|
+
if (scopedMatch?.[1]) {
|
|
12
|
+
return {
|
|
13
|
+
packageName: scopedMatch[1],
|
|
14
|
+
version: scopedMatch[2] ?? null,
|
|
15
|
+
subpath: scopedMatch[3] ?? null,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const unscopedMatch = specifier.match(/^([^/@][^/@]*?)(?:@([^/]+))?(\/.*)?$/);
|
|
20
|
+
if (unscopedMatch?.[1]) {
|
|
21
|
+
return {
|
|
22
|
+
packageName: unscopedMatch[1],
|
|
23
|
+
version: unscopedMatch[2] ?? null,
|
|
24
|
+
subpath: unscopedMatch[3] ?? null,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return null;
|
|
29
|
+
}
|