@esmx/core 3.0.0-rc.116 → 3.0.0-rc.118
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/LICENSE +21 -0
- package/README.md +17 -3
- package/README.zh-CN.md +20 -6
- package/dist/app.mjs +4 -2
- package/dist/cli/cli.d.ts +1 -0
- package/dist/cli/cli.mjs +28 -1
- package/dist/cli/validate.d.ts +15 -0
- package/dist/cli/validate.mjs +139 -0
- package/dist/cli/validate.test.d.ts +1 -0
- package/dist/cli/validate.test.mjs +216 -0
- package/dist/core.d.ts +31 -0
- package/dist/core.mjs +71 -5
- package/dist/declaration/index.d.ts +47 -0
- package/dist/declaration/index.mjs +63 -0
- package/dist/declaration/index.test.d.ts +1 -0
- package/dist/declaration/index.test.mjs +202 -0
- package/dist/declaration/lower.d.ts +11 -0
- package/dist/declaration/lower.mjs +78 -0
- package/dist/declaration/lower.test.d.ts +1 -0
- package/dist/declaration/lower.test.mjs +333 -0
- package/dist/declaration/reader.d.ts +28 -0
- package/dist/declaration/reader.mjs +55 -0
- package/dist/declaration/reinit.e2e.test.d.ts +1 -0
- package/dist/declaration/reinit.e2e.test.mjs +117 -0
- package/dist/declaration/resolver.d.ts +59 -0
- package/dist/declaration/resolver.mjs +430 -0
- package/dist/declaration/resolver.test.d.ts +1 -0
- package/dist/declaration/resolver.test.mjs +1005 -0
- package/dist/declaration/schema.d.ts +89 -0
- package/dist/declaration/schema.mjs +282 -0
- package/dist/declaration/schema.test.d.ts +1 -0
- package/dist/declaration/schema.test.mjs +101 -0
- package/dist/declaration/semver.d.ts +22 -0
- package/dist/declaration/semver.mjs +229 -0
- package/dist/declaration/semver.test.d.ts +1 -0
- package/dist/declaration/semver.test.mjs +87 -0
- package/dist/declaration/test-fixtures.d.ts +18 -0
- package/dist/declaration/test-fixtures.mjs +69 -0
- package/dist/declaration/types.d.ts +58 -0
- package/dist/declaration/types.mjs +21 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.mjs +10 -0
- package/dist/manifest-json.d.ts +61 -0
- package/dist/manifest-json.mjs +68 -5
- package/dist/manifest-json.test.d.ts +1 -0
- package/dist/manifest-json.test.mjs +196 -0
- package/dist/module-config.d.ts +45 -5
- package/dist/module-config.mjs +60 -49
- package/dist/module-config.test.mjs +227 -91
- package/dist/pack-config.d.ts +2 -9
- package/dist/render-context.mjs +22 -5
- package/dist/utils/import-map.d.ts +23 -3
- package/dist/utils/import-map.mjs +38 -1
- package/dist/utils/import-map.test.mjs +228 -0
- package/package.json +9 -6
- package/src/app.ts +5 -2
- package/src/cli/cli.ts +44 -1
- package/src/cli/validate.test.ts +251 -0
- package/src/cli/validate.ts +196 -0
- package/src/core.ts +84 -5
- package/src/declaration/index.test.ts +223 -0
- package/src/declaration/index.ts +135 -0
- package/src/declaration/lower.test.ts +372 -0
- package/src/declaration/lower.ts +135 -0
- package/src/declaration/reader.ts +93 -0
- package/src/declaration/reinit.e2e.test.ts +148 -0
- package/src/declaration/resolver.test.ts +1111 -0
- package/src/declaration/resolver.ts +638 -0
- package/src/declaration/schema.test.ts +118 -0
- package/src/declaration/schema.ts +339 -0
- package/src/declaration/semver.test.ts +101 -0
- package/src/declaration/semver.ts +278 -0
- package/src/declaration/test-fixtures.ts +96 -0
- package/src/declaration/types.ts +71 -0
- package/src/index.ts +28 -1
- package/src/manifest-json.test.ts +236 -0
- package/src/manifest-json.ts +166 -5
- package/src/module-config.test.ts +266 -105
- package/src/module-config.ts +130 -58
- package/src/pack-config.ts +2 -9
- package/src/render-context.ts +34 -6
- package/src/utils/import-map.test.ts +261 -0
- package/src/utils/import-map.ts +92 -6
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import type { EsmxDeclaration } from './types';
|
|
2
|
+
import { type Diagnostic } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* JSON Schema (draft 2020-12) for the package.json `esmx` field.
|
|
5
|
+
* Exported for external tooling and publishing; `validateDeclaration`
|
|
6
|
+
* enforces the same constraints structurally without a schema-validator
|
|
7
|
+
* dependency.
|
|
8
|
+
*/
|
|
9
|
+
export declare const esmxDeclarationSchema: {
|
|
10
|
+
readonly $schema: "https://json-schema.org/draft/2020-12/schema";
|
|
11
|
+
readonly $id: "https://esmx.dev/schema/esmx-declaration.json";
|
|
12
|
+
readonly title: "Esmx module declaration (package.json \"esmx\" field)";
|
|
13
|
+
readonly type: "object";
|
|
14
|
+
readonly additionalProperties: false;
|
|
15
|
+
readonly properties: {
|
|
16
|
+
readonly entry: {
|
|
17
|
+
readonly type: "object";
|
|
18
|
+
readonly additionalProperties: false;
|
|
19
|
+
readonly properties: {
|
|
20
|
+
readonly client: {
|
|
21
|
+
readonly $ref: "#/$defs/relativePath";
|
|
22
|
+
};
|
|
23
|
+
readonly server: {
|
|
24
|
+
readonly $ref: "#/$defs/relativePath";
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
readonly exports: {
|
|
29
|
+
readonly type: "object";
|
|
30
|
+
readonly propertyNames: {
|
|
31
|
+
readonly pattern: "^\\./.+";
|
|
32
|
+
};
|
|
33
|
+
readonly additionalProperties: {
|
|
34
|
+
readonly anyOf: readonly [{
|
|
35
|
+
readonly $ref: "#/$defs/relativePath";
|
|
36
|
+
}, {
|
|
37
|
+
readonly type: "object";
|
|
38
|
+
readonly additionalProperties: false;
|
|
39
|
+
readonly properties: {
|
|
40
|
+
readonly client: {
|
|
41
|
+
readonly $ref: "#/$defs/forkValue";
|
|
42
|
+
};
|
|
43
|
+
readonly server: {
|
|
44
|
+
readonly $ref: "#/$defs/forkValue";
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
}];
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
readonly provides: {
|
|
51
|
+
readonly type: "array";
|
|
52
|
+
readonly items: {
|
|
53
|
+
readonly type: "string";
|
|
54
|
+
readonly minLength: 1;
|
|
55
|
+
};
|
|
56
|
+
};
|
|
57
|
+
readonly uses: {
|
|
58
|
+
readonly type: "array";
|
|
59
|
+
readonly items: {
|
|
60
|
+
readonly type: "string";
|
|
61
|
+
readonly minLength: 1;
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
readonly $defs: {
|
|
66
|
+
readonly relativePath: {
|
|
67
|
+
readonly type: "string";
|
|
68
|
+
readonly pattern: "^\\./.+";
|
|
69
|
+
};
|
|
70
|
+
readonly forkValue: {
|
|
71
|
+
readonly anyOf: readonly [{
|
|
72
|
+
readonly $ref: "#/$defs/relativePath";
|
|
73
|
+
}, {
|
|
74
|
+
readonly const: false;
|
|
75
|
+
}];
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
};
|
|
79
|
+
export interface ValidateDeclarationResult {
|
|
80
|
+
declaration: EsmxDeclaration | null;
|
|
81
|
+
diagnostics: Diagnostic[];
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Structural validator enforcing `esmxDeclarationSchema`. Invalid pieces
|
|
85
|
+
* are dropped and reported; the returned declaration keeps the valid rest
|
|
86
|
+
* so downstream diagnostics stay meaningful. Returns a null declaration
|
|
87
|
+
* only when the field itself is not an object.
|
|
88
|
+
*/
|
|
89
|
+
export declare function validateDeclaration(value: unknown, moduleName: string): ValidateDeclarationResult;
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
import { DiagnosticCode } from "./types.mjs";
|
|
2
|
+
export const esmxDeclarationSchema = {
|
|
3
|
+
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
4
|
+
$id: "https://esmx.dev/schema/esmx-declaration.json",
|
|
5
|
+
title: 'Esmx module declaration (package.json "esmx" field)',
|
|
6
|
+
type: "object",
|
|
7
|
+
additionalProperties: false,
|
|
8
|
+
properties: {
|
|
9
|
+
entry: {
|
|
10
|
+
type: "object",
|
|
11
|
+
additionalProperties: false,
|
|
12
|
+
properties: {
|
|
13
|
+
client: { $ref: "#/$defs/relativePath" },
|
|
14
|
+
server: { $ref: "#/$defs/relativePath" }
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
exports: {
|
|
18
|
+
type: "object",
|
|
19
|
+
propertyNames: { pattern: "^\\./.+" },
|
|
20
|
+
additionalProperties: {
|
|
21
|
+
anyOf: [
|
|
22
|
+
{ $ref: "#/$defs/relativePath" },
|
|
23
|
+
{
|
|
24
|
+
type: "object",
|
|
25
|
+
additionalProperties: false,
|
|
26
|
+
properties: {
|
|
27
|
+
client: { $ref: "#/$defs/forkValue" },
|
|
28
|
+
server: { $ref: "#/$defs/forkValue" }
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
provides: {
|
|
35
|
+
type: "array",
|
|
36
|
+
items: { type: "string", minLength: 1 }
|
|
37
|
+
},
|
|
38
|
+
uses: {
|
|
39
|
+
type: "array",
|
|
40
|
+
items: { type: "string", minLength: 1 }
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
$defs: {
|
|
44
|
+
relativePath: { type: "string", pattern: "^\\./.+" },
|
|
45
|
+
forkValue: {
|
|
46
|
+
anyOf: [{ $ref: "#/$defs/relativePath" }, { const: false }]
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
function schemaError(moduleName, message, fix) {
|
|
51
|
+
return {
|
|
52
|
+
code: DiagnosticCode.E_SCHEMA,
|
|
53
|
+
severity: "error",
|
|
54
|
+
module: moduleName,
|
|
55
|
+
message,
|
|
56
|
+
fix
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
function isRelativePath(value) {
|
|
60
|
+
return typeof value === "string" && /^\.\/.+/.test(value);
|
|
61
|
+
}
|
|
62
|
+
function isRecord(value) {
|
|
63
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
64
|
+
}
|
|
65
|
+
function validateEntry(value, moduleName, diagnostics) {
|
|
66
|
+
if (!isRecord(value)) {
|
|
67
|
+
diagnostics.push(
|
|
68
|
+
schemaError(
|
|
69
|
+
moduleName,
|
|
70
|
+
`"esmx.entry" must be an object with optional "client"/"server" keys.`,
|
|
71
|
+
`Declare entry as { "client": "./src/entry.client.ts", "server": "./src/entry.server.ts" }.`
|
|
72
|
+
)
|
|
73
|
+
);
|
|
74
|
+
return void 0;
|
|
75
|
+
}
|
|
76
|
+
const entry = {};
|
|
77
|
+
for (const key of Object.keys(value)) {
|
|
78
|
+
if (key !== "client" && key !== "server") {
|
|
79
|
+
diagnostics.push(
|
|
80
|
+
schemaError(
|
|
81
|
+
moduleName,
|
|
82
|
+
`"esmx.entry" has unknown key "${key}".`,
|
|
83
|
+
`Only "client" and "server" are allowed in "esmx.entry".`
|
|
84
|
+
)
|
|
85
|
+
);
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
const side = value[key];
|
|
89
|
+
if (!isRelativePath(side)) {
|
|
90
|
+
diagnostics.push(
|
|
91
|
+
schemaError(
|
|
92
|
+
moduleName,
|
|
93
|
+
`"esmx.entry.${key}" must be a relative "./" path, got ${JSON.stringify(side)}.`,
|
|
94
|
+
`Use a relative source path like "./src/entry.${key}.ts".`
|
|
95
|
+
)
|
|
96
|
+
);
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
entry[key] = side;
|
|
100
|
+
}
|
|
101
|
+
return entry;
|
|
102
|
+
}
|
|
103
|
+
function validateExportFork(key, value, moduleName, diagnostics) {
|
|
104
|
+
const fork = {};
|
|
105
|
+
let valid = true;
|
|
106
|
+
for (const sideKey of Object.keys(value)) {
|
|
107
|
+
if (sideKey !== "client" && sideKey !== "server") {
|
|
108
|
+
diagnostics.push(
|
|
109
|
+
schemaError(
|
|
110
|
+
moduleName,
|
|
111
|
+
`"esmx.exports['${key}']" has unknown key "${sideKey}".`,
|
|
112
|
+
`Env-fork export values allow only "client" and "server".`
|
|
113
|
+
)
|
|
114
|
+
);
|
|
115
|
+
valid = false;
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
const side = value[sideKey];
|
|
119
|
+
if (side === false || isRelativePath(side)) {
|
|
120
|
+
fork[sideKey] = side;
|
|
121
|
+
} else {
|
|
122
|
+
diagnostics.push(
|
|
123
|
+
schemaError(
|
|
124
|
+
moduleName,
|
|
125
|
+
`"esmx.exports['${key}'].${sideKey}" must be a relative "./" path or false, got ${JSON.stringify(side)}.`,
|
|
126
|
+
`Point ${sideKey} at a relative source file, or use false to disable that side.`
|
|
127
|
+
)
|
|
128
|
+
);
|
|
129
|
+
valid = false;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return valid ? fork : null;
|
|
133
|
+
}
|
|
134
|
+
function validateExports(value, moduleName, diagnostics) {
|
|
135
|
+
if (!isRecord(value)) {
|
|
136
|
+
diagnostics.push(
|
|
137
|
+
schemaError(
|
|
138
|
+
moduleName,
|
|
139
|
+
`"esmx.exports" must be an object mapping "./<name>" subpaths to source files.`,
|
|
140
|
+
`Declare exports as { "./widget": "./src/widget.ts" }.`
|
|
141
|
+
)
|
|
142
|
+
);
|
|
143
|
+
return void 0;
|
|
144
|
+
}
|
|
145
|
+
const exports = {};
|
|
146
|
+
for (const [key, exportValue] of Object.entries(value)) {
|
|
147
|
+
if (!/^\.\/.+/.test(key)) {
|
|
148
|
+
diagnostics.push(
|
|
149
|
+
schemaError(
|
|
150
|
+
moduleName,
|
|
151
|
+
`"esmx.exports" key "${key}" must be a "./<name>" subpath.`,
|
|
152
|
+
`Rename the key to "./${key.replace(/^\.?\/?/, "")}".`
|
|
153
|
+
)
|
|
154
|
+
);
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
if (isRelativePath(exportValue)) {
|
|
158
|
+
exports[key] = exportValue;
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
if (isRecord(exportValue)) {
|
|
162
|
+
const fork = validateExportFork(
|
|
163
|
+
key,
|
|
164
|
+
exportValue,
|
|
165
|
+
moduleName,
|
|
166
|
+
diagnostics
|
|
167
|
+
);
|
|
168
|
+
if (fork) {
|
|
169
|
+
exports[key] = fork;
|
|
170
|
+
}
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
diagnostics.push(
|
|
174
|
+
schemaError(
|
|
175
|
+
moduleName,
|
|
176
|
+
`"esmx.exports['${key}']" must be a relative "./" path or a { client, server } fork, got ${JSON.stringify(exportValue)}.`,
|
|
177
|
+
`Point the export at a relative source file like "./src/widget.ts".`
|
|
178
|
+
)
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
return exports;
|
|
182
|
+
}
|
|
183
|
+
function validateNameArray(field, value, moduleName, diagnostics) {
|
|
184
|
+
if (!Array.isArray(value)) {
|
|
185
|
+
diagnostics.push(
|
|
186
|
+
schemaError(
|
|
187
|
+
moduleName,
|
|
188
|
+
`"esmx.${field}" must be an array of non-empty strings.`,
|
|
189
|
+
`Declare ${field} as a string array, e.g. ["vue"].`
|
|
190
|
+
)
|
|
191
|
+
);
|
|
192
|
+
return void 0;
|
|
193
|
+
}
|
|
194
|
+
const names = [];
|
|
195
|
+
for (const item of value) {
|
|
196
|
+
if (typeof item !== "string" || item.length === 0) {
|
|
197
|
+
diagnostics.push(
|
|
198
|
+
schemaError(
|
|
199
|
+
moduleName,
|
|
200
|
+
`"esmx.${field}" entries must be non-empty strings, got ${JSON.stringify(item)}.`,
|
|
201
|
+
`Remove or replace the invalid ${field} entry.`
|
|
202
|
+
)
|
|
203
|
+
);
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
names.push(item);
|
|
207
|
+
}
|
|
208
|
+
return names;
|
|
209
|
+
}
|
|
210
|
+
export function validateDeclaration(value, moduleName) {
|
|
211
|
+
const diagnostics = [];
|
|
212
|
+
if (!isRecord(value)) {
|
|
213
|
+
diagnostics.push(
|
|
214
|
+
schemaError(
|
|
215
|
+
moduleName,
|
|
216
|
+
`"esmx" field must be an object, got ${JSON.stringify(value)}.`,
|
|
217
|
+
`Declare protocol facts as an object: { "entry": ..., "exports": ..., "provides": ..., "uses": ... }.`
|
|
218
|
+
)
|
|
219
|
+
);
|
|
220
|
+
return { declaration: null, diagnostics };
|
|
221
|
+
}
|
|
222
|
+
const declaration = {};
|
|
223
|
+
for (const key of Object.keys(value)) {
|
|
224
|
+
switch (key) {
|
|
225
|
+
case "entry": {
|
|
226
|
+
const entry = validateEntry(
|
|
227
|
+
value.entry,
|
|
228
|
+
moduleName,
|
|
229
|
+
diagnostics
|
|
230
|
+
);
|
|
231
|
+
if (entry) {
|
|
232
|
+
declaration.entry = entry;
|
|
233
|
+
}
|
|
234
|
+
break;
|
|
235
|
+
}
|
|
236
|
+
case "exports": {
|
|
237
|
+
const exports = validateExports(
|
|
238
|
+
value.exports,
|
|
239
|
+
moduleName,
|
|
240
|
+
diagnostics
|
|
241
|
+
);
|
|
242
|
+
if (exports) {
|
|
243
|
+
declaration.exports = exports;
|
|
244
|
+
}
|
|
245
|
+
break;
|
|
246
|
+
}
|
|
247
|
+
case "provides": {
|
|
248
|
+
const provides = validateNameArray(
|
|
249
|
+
"provides",
|
|
250
|
+
value.provides,
|
|
251
|
+
moduleName,
|
|
252
|
+
diagnostics
|
|
253
|
+
);
|
|
254
|
+
if (provides) {
|
|
255
|
+
declaration.provides = provides;
|
|
256
|
+
}
|
|
257
|
+
break;
|
|
258
|
+
}
|
|
259
|
+
case "uses": {
|
|
260
|
+
const uses = validateNameArray(
|
|
261
|
+
"uses",
|
|
262
|
+
value.uses,
|
|
263
|
+
moduleName,
|
|
264
|
+
diagnostics
|
|
265
|
+
);
|
|
266
|
+
if (uses) {
|
|
267
|
+
declaration.uses = uses;
|
|
268
|
+
}
|
|
269
|
+
break;
|
|
270
|
+
}
|
|
271
|
+
default:
|
|
272
|
+
diagnostics.push(
|
|
273
|
+
schemaError(
|
|
274
|
+
moduleName,
|
|
275
|
+
`"esmx" has unknown key "${key}".`,
|
|
276
|
+
`Allowed keys are "entry", "exports", "provides", "uses".`
|
|
277
|
+
)
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
return { declaration, diagnostics };
|
|
282
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { esmxDeclarationSchema, validateDeclaration } from "./schema.mjs";
|
|
3
|
+
import { DiagnosticCode } from "./types.mjs";
|
|
4
|
+
describe("esmxDeclarationSchema", () => {
|
|
5
|
+
it("should be a draft 2020-12 schema for the esmx field", () => {
|
|
6
|
+
expect(esmxDeclarationSchema.$schema).toBe(
|
|
7
|
+
"https://json-schema.org/draft/2020-12/schema"
|
|
8
|
+
);
|
|
9
|
+
expect(Object.keys(esmxDeclarationSchema.properties)).toEqual([
|
|
10
|
+
"entry",
|
|
11
|
+
"exports",
|
|
12
|
+
"provides",
|
|
13
|
+
"uses"
|
|
14
|
+
]);
|
|
15
|
+
expect(esmxDeclarationSchema.additionalProperties).toBe(false);
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
describe("validateDeclaration", () => {
|
|
19
|
+
it("should accept a full valid declaration without diagnostics", () => {
|
|
20
|
+
const input = {
|
|
21
|
+
entry: {
|
|
22
|
+
client: "./src/entry.client.ts",
|
|
23
|
+
server: "./src/entry.server.ts"
|
|
24
|
+
},
|
|
25
|
+
exports: {
|
|
26
|
+
"./ui": "./src/ui/index.ts",
|
|
27
|
+
"./store": {
|
|
28
|
+
client: "./src/store.client.ts",
|
|
29
|
+
server: false
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
provides: ["vue", "@esmx/router"],
|
|
33
|
+
uses: ["shared"]
|
|
34
|
+
};
|
|
35
|
+
const result = validateDeclaration(input, "cart");
|
|
36
|
+
expect(result.diagnostics).toEqual([]);
|
|
37
|
+
expect(result.declaration).toEqual(input);
|
|
38
|
+
});
|
|
39
|
+
it("should reject a non-object esmx field", () => {
|
|
40
|
+
const result = validateDeclaration("nope", "cart");
|
|
41
|
+
expect(result.declaration).toBeNull();
|
|
42
|
+
expect(result.diagnostics).toHaveLength(1);
|
|
43
|
+
expect(result.diagnostics[0].code).toBe(DiagnosticCode.E_SCHEMA);
|
|
44
|
+
expect(result.diagnostics[0].severity).toBe("error");
|
|
45
|
+
expect(result.diagnostics[0].module).toBe("cart");
|
|
46
|
+
});
|
|
47
|
+
it("should reject export keys without a ./ subpath prefix", () => {
|
|
48
|
+
const result = validateDeclaration(
|
|
49
|
+
{ exports: { widget: "./src/widget.ts" } },
|
|
50
|
+
"cart"
|
|
51
|
+
);
|
|
52
|
+
expect(result.diagnostics).toHaveLength(1);
|
|
53
|
+
expect(result.diagnostics[0].code).toBe(DiagnosticCode.E_SCHEMA);
|
|
54
|
+
expect(result.diagnostics[0].message).toContain('"widget"');
|
|
55
|
+
expect(result.declaration?.exports).toEqual({});
|
|
56
|
+
});
|
|
57
|
+
it("should reject non-relative export file paths", () => {
|
|
58
|
+
const result = validateDeclaration(
|
|
59
|
+
{ exports: { "./widget": "src/widget.ts" } },
|
|
60
|
+
"cart"
|
|
61
|
+
);
|
|
62
|
+
expect(result.diagnostics).toHaveLength(1);
|
|
63
|
+
expect(result.diagnostics[0].code).toBe(DiagnosticCode.E_SCHEMA);
|
|
64
|
+
expect(result.diagnostics[0].message).toContain("./");
|
|
65
|
+
});
|
|
66
|
+
it("should reject wrong types in provides and uses", () => {
|
|
67
|
+
const result = validateDeclaration(
|
|
68
|
+
{ provides: "vue", uses: ["shared", "", 42] },
|
|
69
|
+
"cart"
|
|
70
|
+
);
|
|
71
|
+
const codes = result.diagnostics.map((d) => d.code);
|
|
72
|
+
expect(codes).toEqual([
|
|
73
|
+
DiagnosticCode.E_SCHEMA,
|
|
74
|
+
DiagnosticCode.E_SCHEMA,
|
|
75
|
+
DiagnosticCode.E_SCHEMA
|
|
76
|
+
]);
|
|
77
|
+
expect(result.declaration?.provides).toBeUndefined();
|
|
78
|
+
expect(result.declaration?.uses).toEqual(["shared"]);
|
|
79
|
+
});
|
|
80
|
+
it("should reject non-relative entry paths and unknown keys", () => {
|
|
81
|
+
const result = validateDeclaration(
|
|
82
|
+
{
|
|
83
|
+
entry: { client: "src/entry.client.ts", node: "./x.ts" },
|
|
84
|
+
bogus: true
|
|
85
|
+
},
|
|
86
|
+
"cart"
|
|
87
|
+
);
|
|
88
|
+
const codes = result.diagnostics.map((d) => d.code);
|
|
89
|
+
expect(codes).toHaveLength(3);
|
|
90
|
+
expect(new Set(codes)).toEqual(/* @__PURE__ */ new Set([DiagnosticCode.E_SCHEMA]));
|
|
91
|
+
});
|
|
92
|
+
it("should reject invalid env-fork values", () => {
|
|
93
|
+
const result = validateDeclaration(
|
|
94
|
+
{ exports: { "./store": { client: true, server: "./s.ts" } } },
|
|
95
|
+
"cart"
|
|
96
|
+
);
|
|
97
|
+
expect(result.diagnostics).toHaveLength(1);
|
|
98
|
+
expect(result.diagnostics[0].code).toBe(DiagnosticCode.E_SCHEMA);
|
|
99
|
+
expect(result.declaration?.exports).toEqual({});
|
|
100
|
+
});
|
|
101
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal semver-range satisfaction used by the declaration resolver.
|
|
3
|
+
*
|
|
4
|
+
* Supports the subset the RFC validation needs: exact versions, `^`, `~`,
|
|
5
|
+
* comparators (`>=`, `>`, `<=`, `<`, `=`), `*`, x-ranges (`1.x`, `1.2.x`,
|
|
6
|
+
* `1`, `1.2`), space-separated AND clauses and `||` OR clauses. Anything
|
|
7
|
+
* else (workspace:, file:, link:, npm aliases, hyphen ranges) is reported
|
|
8
|
+
* as unparsable so callers can skip the gate per RFC §11.
|
|
9
|
+
*/
|
|
10
|
+
export interface SemverVersion {
|
|
11
|
+
major: number;
|
|
12
|
+
minor: number;
|
|
13
|
+
patch: number;
|
|
14
|
+
prerelease: string[];
|
|
15
|
+
}
|
|
16
|
+
export declare function parseSemver(input: string): SemverVersion | null;
|
|
17
|
+
export declare function compareSemver(a: SemverVersion, b: SemverVersion): number;
|
|
18
|
+
/**
|
|
19
|
+
* Returns true/false when both version and range are understood,
|
|
20
|
+
* null when either is unparsable (caller skips the gate, RFC §11).
|
|
21
|
+
*/
|
|
22
|
+
export declare function satisfiesRange(version: string, range: string): boolean | null;
|