@valbuild/language-server 0.135.0 → 0.136.1
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 +50 -0
- package/dist/declarations/src/diagnostics.d.ts +16 -2
- package/dist/declarations/src/valModulesRegistry.d.ts +12 -0
- package/dist/valbuild-language-server.cjs.dev.js +124 -20
- package/dist/valbuild-language-server.cjs.prod.js +124 -20
- package/dist/valbuild-language-server.esm.js +125 -21
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,55 @@
|
|
|
1
1
|
# @valbuild/language-server
|
|
2
2
|
|
|
3
|
+
## 0.136.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [[`e673b43`](https://github.com/valbuild/val/commit/e673b43feaf48b7f30881d6786c858a0cb6a44a2), [`5c82928`](https://github.com/valbuild/val/commit/5c82928992d29f133f81cf0704a273c0449b18f1), [`4450be5`](https://github.com/valbuild/val/commit/4450be570ec3a66d7d98ba273c333ca2d2f163d9), [`d2f385a`](https://github.com/valbuild/val/commit/d2f385a04978992a5036379235e447ba7775faef)]:
|
|
8
|
+
- @valbuild/shared@0.136.1
|
|
9
|
+
- @valbuild/server@0.136.1
|
|
10
|
+
- @valbuild/core@0.136.1
|
|
11
|
+
|
|
12
|
+
## 0.136.0
|
|
13
|
+
|
|
14
|
+
### Patch Changes
|
|
15
|
+
|
|
16
|
+
- [#699](https://github.com/valbuild/val/pull/699) [`1d72d00`](https://github.com/valbuild/val/commit/1d72d00a1b036959ae0f1540121701cbb2694dcb) Thanks [@freekh](https://github.com/freekh)! - A `*.val.ts` with no default export is no longer reported as a missing module
|
|
17
|
+
|
|
18
|
+
`*.val.ts` is a naming convention, not a promise: plenty of files under it hold
|
|
19
|
+
only the schemas and helpers the modules beside them import. Every one of those
|
|
20
|
+
was getting two errors in the editor — `Module '…' was not found in
|
|
21
|
+
val.modules` and `… is not registered in val.modules, so Val will not serve it`
|
|
22
|
+
— both on line 1, telling their author to register a file that has nothing to
|
|
23
|
+
register. `val validate` has never reported them; only the editor did.
|
|
24
|
+
|
|
25
|
+
The default export is what makes a `*.val.ts` a module, so that is what the
|
|
26
|
+
diagnostic now asks about:
|
|
27
|
+
|
|
28
|
+
- **No default export → nothing is reported.** The file is not a module, so it
|
|
29
|
+
is not a module Val is failing to serve.
|
|
30
|
+
- **A default export → one diagnostic, on the `export default` itself** rather
|
|
31
|
+
than on line 1, so it is next to the thing that has to change, and the
|
|
32
|
+
duplicate fatal beside it is gone. The message now gives both remedies: add
|
|
33
|
+
the file to `val.modules`, or export what it holds by name instead. The "Val:
|
|
34
|
+
register … in val.modules" quick fix is offered there.
|
|
35
|
+
|
|
36
|
+
The rule is `findDefaultExport` in `@valbuild/server`, which `val validate`
|
|
37
|
+
already used to decide the same question — so the editor and the CLI now agree
|
|
38
|
+
about which files are modules, including the cases that are easy to get wrong
|
|
39
|
+
(`export * from …` carries no default; `export type { T as default }` and
|
|
40
|
+
`export default interface T {}` are both gone after transpilation).
|
|
41
|
+
|
|
42
|
+
Because the diagnostic now replaces a module's own findings rather than adding
|
|
43
|
+
to them, the editor also stops guessing about registration it cannot read: a
|
|
44
|
+
`val.modules` that registers modules through a tsconfig path alias
|
|
45
|
+
(`import("_/content/page.val")`), or that builds its list in another file, is no
|
|
46
|
+
longer taken to register nothing.
|
|
47
|
+
|
|
48
|
+
- Updated dependencies [[`f3a4bb7`](https://github.com/valbuild/val/commit/f3a4bb7aea604943b92545c122243798c759715c), [`1d72d00`](https://github.com/valbuild/val/commit/1d72d00a1b036959ae0f1540121701cbb2694dcb)]:
|
|
49
|
+
- @valbuild/core@0.136.0
|
|
50
|
+
- @valbuild/server@0.136.0
|
|
51
|
+
- @valbuild/shared@0.136.0
|
|
52
|
+
|
|
3
53
|
## 0.135.0
|
|
4
54
|
|
|
5
55
|
### Patch Changes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type ModuleFilePath, type SourcePath, type ValidationError, type ValidationFix } from "@valbuild/core";
|
|
2
2
|
import { type SchemaSourceSnapshot } from "@valbuild/shared/internal";
|
|
3
|
-
import { Diagnostic, DiagnosticSeverity } from "vscode-languageserver";
|
|
3
|
+
import { Diagnostic, DiagnosticSeverity, type Range } from "vscode-languageserver";
|
|
4
4
|
import { type MediaCheckVerdict } from "./mediaChecks.js";
|
|
5
5
|
import type { ValModuleContent } from "./ValProject.js";
|
|
6
6
|
/** Marks diagnostics as ours, so a client can filter on it. */
|
|
@@ -130,9 +130,23 @@ export declare function createProjectErrorDiagnostic({ moduleFilePath, message,
|
|
|
130
130
|
*
|
|
131
131
|
* Val only serves modules listed there, so an unregistered module silently does
|
|
132
132
|
* nothing — worth surfacing even though it is not a validation error.
|
|
133
|
+
*
|
|
134
|
+
* Reported on the `export default`, which is also why a file that has none gets
|
|
135
|
+
* no diagnostic at all (see `findValModuleDefinition`): the default export is
|
|
136
|
+
* the thing that makes a `*.val.ts` a module, so it is the thing being asked
|
|
137
|
+
* about, and pointing at line 1 of a file full of shared schemas asked its
|
|
138
|
+
* author to register something that does not exist.
|
|
139
|
+
*
|
|
140
|
+
* The message carries both remedies for the same reason. Only one of them is
|
|
141
|
+
* "add it to val.modules"; the other is to stop default-exporting, which is how
|
|
142
|
+
* a helper that landed in the module slot is fixed. Never "wrap it in
|
|
143
|
+
* `c.define`" — that turns it into an unregistered module, i.e. straight back
|
|
144
|
+
* to here.
|
|
133
145
|
*/
|
|
134
|
-
export declare function createMissingModuleDiagnostic({ moduleFilePath, }: {
|
|
146
|
+
export declare function createMissingModuleDiagnostic({ moduleFilePath, range, }: {
|
|
135
147
|
moduleFilePath: ModuleFilePath;
|
|
148
|
+
/** The module's `export default`, from `findValModuleDefinition`. */
|
|
149
|
+
range: Range;
|
|
136
150
|
}): Diagnostic;
|
|
137
151
|
export declare function isGalleryCheckFix(fix: string): boolean;
|
|
138
152
|
/**
|
|
@@ -20,6 +20,18 @@ export declare function findRegisteredModuleSpecifiers(sourceFile: ts.SourceFile
|
|
|
20
20
|
/**
|
|
21
21
|
* Whether `moduleFilePath` is registered by the given `val.modules` file.
|
|
22
22
|
*
|
|
23
|
+
* Answers `true` whenever it cannot tell, which is the same safe direction
|
|
24
|
+
* {@link findRegisteredModuleSpecifiers} takes and matters more here than it
|
|
25
|
+
* looks: the caller publishes the missing-module diagnostic *instead of* the
|
|
26
|
+
* module's own diagnostics, so a false "unregistered" does not just add noise,
|
|
27
|
+
* it hides every real finding in the file. Two things it cannot tell:
|
|
28
|
+
*
|
|
29
|
+
* - a `val.modules` with no dynamic imports at all, which is what a registry
|
|
30
|
+
* that builds its list somewhere else looks like from here;
|
|
31
|
+
* - a non-relative specifier naming a `.val` module (`_/content/page.val`),
|
|
32
|
+
* which is a tsconfig path alias — a shape `loadValModules` resolves and
|
|
33
|
+
* this file, having no compiler options, cannot.
|
|
34
|
+
*
|
|
23
35
|
* @param valModulesDir directory containing the val.modules file, relative to
|
|
24
36
|
* the Val root (`""` when it sits at the root).
|
|
25
37
|
* @param moduleFilePath the module's path, Val-style: root-relative, leading
|
|
@@ -183,7 +183,7 @@ var packageJson = {
|
|
|
183
183
|
"lsp",
|
|
184
184
|
"language-server"
|
|
185
185
|
],
|
|
186
|
-
version: "0.
|
|
186
|
+
version: "0.136.1",
|
|
187
187
|
bin: {
|
|
188
188
|
"val-language-server": "./bin.js"
|
|
189
189
|
},
|
|
@@ -1627,11 +1627,24 @@ function createProjectErrorDiagnostic({
|
|
|
1627
1627
|
*
|
|
1628
1628
|
* Val only serves modules listed there, so an unregistered module silently does
|
|
1629
1629
|
* nothing — worth surfacing even though it is not a validation error.
|
|
1630
|
+
*
|
|
1631
|
+
* Reported on the `export default`, which is also why a file that has none gets
|
|
1632
|
+
* no diagnostic at all (see `findValModuleDefinition`): the default export is
|
|
1633
|
+
* the thing that makes a `*.val.ts` a module, so it is the thing being asked
|
|
1634
|
+
* about, and pointing at line 1 of a file full of shared schemas asked its
|
|
1635
|
+
* author to register something that does not exist.
|
|
1636
|
+
*
|
|
1637
|
+
* The message carries both remedies for the same reason. Only one of them is
|
|
1638
|
+
* "add it to val.modules"; the other is to stop default-exporting, which is how
|
|
1639
|
+
* a helper that landed in the module slot is fixed. Never "wrap it in
|
|
1640
|
+
* `c.define`" — that turns it into an unregistered module, i.e. straight back
|
|
1641
|
+
* to here.
|
|
1630
1642
|
*/
|
|
1631
1643
|
function createMissingModuleDiagnostic({
|
|
1632
|
-
moduleFilePath
|
|
1644
|
+
moduleFilePath,
|
|
1645
|
+
range
|
|
1633
1646
|
}) {
|
|
1634
|
-
return build(
|
|
1647
|
+
return build(range, `${moduleFilePath} is not registered in val.modules, so Val will not serve it. Add it there — or, if this file is not meant to be a Val module, export what it holds by name instead: a default export is what makes a *.val.ts a module.`, {
|
|
1635
1648
|
code: "val/missing-module",
|
|
1636
1649
|
sourcePath: moduleFilePath
|
|
1637
1650
|
});
|
|
@@ -2243,6 +2256,18 @@ function findRegisteredModuleSpecifiers(sourceFile) {
|
|
|
2243
2256
|
/**
|
|
2244
2257
|
* Whether `moduleFilePath` is registered by the given `val.modules` file.
|
|
2245
2258
|
*
|
|
2259
|
+
* Answers `true` whenever it cannot tell, which is the same safe direction
|
|
2260
|
+
* {@link findRegisteredModuleSpecifiers} takes and matters more here than it
|
|
2261
|
+
* looks: the caller publishes the missing-module diagnostic *instead of* the
|
|
2262
|
+
* module's own diagnostics, so a false "unregistered" does not just add noise,
|
|
2263
|
+
* it hides every real finding in the file. Two things it cannot tell:
|
|
2264
|
+
*
|
|
2265
|
+
* - a `val.modules` with no dynamic imports at all, which is what a registry
|
|
2266
|
+
* that builds its list somewhere else looks like from here;
|
|
2267
|
+
* - a non-relative specifier naming a `.val` module (`_/content/page.val`),
|
|
2268
|
+
* which is a tsconfig path alias — a shape `loadValModules` resolves and
|
|
2269
|
+
* this file, having no compiler options, cannot.
|
|
2270
|
+
*
|
|
2246
2271
|
* @param valModulesDir directory containing the val.modules file, relative to
|
|
2247
2272
|
* the Val root (`""` when it sits at the root).
|
|
2248
2273
|
* @param moduleFilePath the module's path, Val-style: root-relative, leading
|
|
@@ -2254,11 +2279,23 @@ function isModuleRegistered({
|
|
|
2254
2279
|
moduleFilePath
|
|
2255
2280
|
}) {
|
|
2256
2281
|
const target = stripValModuleExtension(moduleFilePath);
|
|
2257
|
-
|
|
2282
|
+
const specifiers = findRegisteredModuleSpecifiers(sourceFile);
|
|
2283
|
+
if (specifiers.length === 0) {
|
|
2284
|
+
return true;
|
|
2285
|
+
}
|
|
2286
|
+
return specifiers.some(specifier => {
|
|
2287
|
+
const stripped = stripValModuleExtension(specifier);
|
|
2258
2288
|
// Specifiers are written relative to the val.modules file and normally omit
|
|
2259
2289
|
// the extension ("./content/page.val").
|
|
2260
|
-
|
|
2261
|
-
|
|
2290
|
+
if (specifier.startsWith(".")) {
|
|
2291
|
+
return path__default["default"].posix.normalize(path__default["default"].posix.join("/", valModulesDir, stripped)) === target;
|
|
2292
|
+
}
|
|
2293
|
+
if (specifier.startsWith("/")) {
|
|
2294
|
+
return stripped === target;
|
|
2295
|
+
}
|
|
2296
|
+
// A path alias, which needs the project's tsconfig to resolve. A bare
|
|
2297
|
+
// package import is not one of these, so it stays a plain non-match.
|
|
2298
|
+
return stripped.endsWith(".val");
|
|
2262
2299
|
});
|
|
2263
2300
|
}
|
|
2264
2301
|
|
|
@@ -3668,6 +3705,60 @@ function keysOfKeyOf(schema, snapshot) {
|
|
|
3668
3705
|
}
|
|
3669
3706
|
}
|
|
3670
3707
|
|
|
3708
|
+
/**
|
|
3709
|
+
* Where a file declares its Val module, or `undefined` when it declares none.
|
|
3710
|
+
*
|
|
3711
|
+
* `*.val.ts` is a naming convention, not a guarantee: a file under it may hold
|
|
3712
|
+
* nothing but the schemas and helpers the modules beside it import. The default
|
|
3713
|
+
* export is what separates the two, and `findDefaultExport` in
|
|
3714
|
+
* `@valbuild/server` is the one implementation of that rule — the same one
|
|
3715
|
+
* `val validate` uses to decide whether an unregistered file is worth reporting
|
|
3716
|
+
* at all. It is imported rather than reimplemented because the subtleties are
|
|
3717
|
+
* not obvious (a star re-export carries no default, a type-only export is gone
|
|
3718
|
+
* after transpilation), and two copies would answer differently the first time
|
|
3719
|
+
* one of them was fixed.
|
|
3720
|
+
*
|
|
3721
|
+
* Only the `export default` keywords are returned, never the whole definition:
|
|
3722
|
+
* a module's source is most of its file, and a diagnostic spanning that paints
|
|
3723
|
+
* the file red.
|
|
3724
|
+
*/
|
|
3725
|
+
function findValModuleDefinition(sourceFile) {
|
|
3726
|
+
const statement = server.findDefaultExport(sourceFile);
|
|
3727
|
+
if (!statement) {
|
|
3728
|
+
return undefined;
|
|
3729
|
+
}
|
|
3730
|
+
const start = statement.getStart(sourceFile);
|
|
3731
|
+
return {
|
|
3732
|
+
start: sourceFile.getLineAndCharacterOfPosition(start),
|
|
3733
|
+
end: sourceFile.getLineAndCharacterOfPosition(keywordsEnd(sourceFile, statement, start))
|
|
3734
|
+
};
|
|
3735
|
+
}
|
|
3736
|
+
|
|
3737
|
+
/**
|
|
3738
|
+
* The end of the `export default` a statement opens with.
|
|
3739
|
+
*
|
|
3740
|
+
* Three shapes reach this, and only the first two can be narrowed: `export
|
|
3741
|
+
* default <expr>`, where everything before the expression is the keywords;
|
|
3742
|
+
* `export default function f() {}` and `export default class C {}`, where they
|
|
3743
|
+
* are modifiers; and `export { x as default }`, which has no `default` keyword
|
|
3744
|
+
* to stop at and is short enough to underline whole.
|
|
3745
|
+
*/
|
|
3746
|
+
function keywordsEnd(sourceFile, statement, start) {
|
|
3747
|
+
if (ts__default["default"].isExportAssignment(statement)) {
|
|
3748
|
+
// Up to the exported expression is `export default` plus whitespace (and,
|
|
3749
|
+
// rarely, a comment); trimming the tail keeps the underline on the keywords.
|
|
3750
|
+
return start + sourceFile.text.slice(start, statement.expression.getStart(sourceFile)).trimEnd().length;
|
|
3751
|
+
}
|
|
3752
|
+
const defaultModifier = ts__default["default"].canHaveModifiers(statement) ? (ts__default["default"].getModifiers(statement) ?? []).find(modifier => modifier.kind === ts__default["default"].SyntaxKind.DefaultKeyword) : undefined;
|
|
3753
|
+
return defaultModifier?.end ?? statement.end;
|
|
3754
|
+
}
|
|
3755
|
+
|
|
3756
|
+
/** Parse a Val module's text the way {@link findValModuleDefinition} needs it. */
|
|
3757
|
+
function parseValModule(moduleFilePath, text) {
|
|
3758
|
+
// Parent pointers: `getStart` walks them to skip leading trivia.
|
|
3759
|
+
return ts__default["default"].createSourceFile(moduleFilePath, text, ts__default["default"].ScriptTarget.ES2020, true);
|
|
3760
|
+
}
|
|
3761
|
+
|
|
3671
3762
|
/**
|
|
3672
3763
|
* The two remedies for a gallery-backed field pointing at something its gallery
|
|
3673
3764
|
* does not have.
|
|
@@ -4127,15 +4218,14 @@ function applyEnvOverrides(options) {
|
|
|
4127
4218
|
const PROJECT_WIDE_FILE_RE = /[/\\]val\.(modules|config)\.(ts|js)$/;
|
|
4128
4219
|
|
|
4129
4220
|
/**
|
|
4130
|
-
*
|
|
4221
|
+
* Whether `val.modules` leaves this module out.
|
|
4131
4222
|
*
|
|
4132
4223
|
* Reads `val.modules.{ts,js}` on demand, through the editor's buffer when it is
|
|
4133
4224
|
* open: adding a module to the registry must clear the diagnostic straight away,
|
|
4134
|
-
* not only once the user saves.
|
|
4135
|
-
*
|
|
4136
|
-
* module.
|
|
4225
|
+
* not only once the user saves. Answers `false` when no such file exists — that
|
|
4226
|
+
* is a project-level problem, not something to blame on an individual module.
|
|
4137
4227
|
*/
|
|
4138
|
-
function
|
|
4228
|
+
function isModuleUnregistered(valRoot, moduleFilePath, read) {
|
|
4139
4229
|
for (const candidate of ["val.modules.ts", "val.modules.js"]) {
|
|
4140
4230
|
const file = path__default["default"].join(valRoot, candidate);
|
|
4141
4231
|
let text = read(file);
|
|
@@ -4146,16 +4236,13 @@ function findMissingModuleDiagnostic(valRoot, moduleFilePath, read) {
|
|
|
4146
4236
|
continue;
|
|
4147
4237
|
}
|
|
4148
4238
|
}
|
|
4149
|
-
|
|
4239
|
+
return !isModuleRegistered({
|
|
4150
4240
|
sourceFile: ts__default["default"].createSourceFile(file, text, ts__default["default"].ScriptTarget.ES2020),
|
|
4151
4241
|
valModulesDir: "",
|
|
4152
4242
|
moduleFilePath
|
|
4153
4243
|
});
|
|
4154
|
-
return registered ? undefined : createMissingModuleDiagnostic({
|
|
4155
|
-
moduleFilePath
|
|
4156
|
-
});
|
|
4157
4244
|
}
|
|
4158
|
-
return
|
|
4245
|
+
return false;
|
|
4159
4246
|
}
|
|
4160
4247
|
|
|
4161
4248
|
/**
|
|
@@ -4220,6 +4307,27 @@ function createValLanguageServer(connection) {
|
|
|
4220
4307
|
return;
|
|
4221
4308
|
}
|
|
4222
4309
|
try {
|
|
4310
|
+
// An unregistered file is answered from its own text alone, before the
|
|
4311
|
+
// project is consulted at all. Val cannot have evaluated it -- it is not
|
|
4312
|
+
// in the graph `val.modules` describes -- so everything downstream has
|
|
4313
|
+
// only the one thing to say about it, and said it twice: `Service.get`
|
|
4314
|
+
// reports `Module '...' was not found in val.modules` as a fatal on line
|
|
4315
|
+
// 1, beside this diagnostic saying the same. And when the file has no
|
|
4316
|
+
// `export default` neither of them is a finding at all: it is a
|
|
4317
|
+
// `*.val.ts` holding the schemas the modules beside it import, which Val
|
|
4318
|
+
// never serves and there is nothing to register.
|
|
4319
|
+
const unregistered = isModuleUnregistered(project.valRoot, moduleFilePath, readOpenDocument);
|
|
4320
|
+
if (unregistered) {
|
|
4321
|
+
const definition = findValModuleDefinition(parseValModule(moduleFilePath, document.getText()));
|
|
4322
|
+
connection.sendDiagnostics({
|
|
4323
|
+
uri,
|
|
4324
|
+
diagnostics: definition ? [createMissingModuleDiagnostic({
|
|
4325
|
+
moduleFilePath,
|
|
4326
|
+
range: definition
|
|
4327
|
+
})] : []
|
|
4328
|
+
});
|
|
4329
|
+
return;
|
|
4330
|
+
}
|
|
4223
4331
|
// The module's own content changed, so any cached result is stale.
|
|
4224
4332
|
project.invalidate(moduleFilePath);
|
|
4225
4333
|
const result = await project.getModule(moduleFilePath);
|
|
@@ -4283,10 +4391,6 @@ function createValLanguageServer(connection) {
|
|
|
4283
4391
|
mediaChecks
|
|
4284
4392
|
} : {})
|
|
4285
4393
|
});
|
|
4286
|
-
const unregistered = findMissingModuleDiagnostic(project.valRoot, moduleFilePath, readOpenDocument);
|
|
4287
|
-
if (unregistered) {
|
|
4288
|
-
diagnostics.push(unregistered);
|
|
4289
|
-
}
|
|
4290
4394
|
connection.sendDiagnostics({
|
|
4291
4395
|
uri,
|
|
4292
4396
|
diagnostics
|
|
@@ -183,7 +183,7 @@ var packageJson = {
|
|
|
183
183
|
"lsp",
|
|
184
184
|
"language-server"
|
|
185
185
|
],
|
|
186
|
-
version: "0.
|
|
186
|
+
version: "0.136.1",
|
|
187
187
|
bin: {
|
|
188
188
|
"val-language-server": "./bin.js"
|
|
189
189
|
},
|
|
@@ -1627,11 +1627,24 @@ function createProjectErrorDiagnostic({
|
|
|
1627
1627
|
*
|
|
1628
1628
|
* Val only serves modules listed there, so an unregistered module silently does
|
|
1629
1629
|
* nothing — worth surfacing even though it is not a validation error.
|
|
1630
|
+
*
|
|
1631
|
+
* Reported on the `export default`, which is also why a file that has none gets
|
|
1632
|
+
* no diagnostic at all (see `findValModuleDefinition`): the default export is
|
|
1633
|
+
* the thing that makes a `*.val.ts` a module, so it is the thing being asked
|
|
1634
|
+
* about, and pointing at line 1 of a file full of shared schemas asked its
|
|
1635
|
+
* author to register something that does not exist.
|
|
1636
|
+
*
|
|
1637
|
+
* The message carries both remedies for the same reason. Only one of them is
|
|
1638
|
+
* "add it to val.modules"; the other is to stop default-exporting, which is how
|
|
1639
|
+
* a helper that landed in the module slot is fixed. Never "wrap it in
|
|
1640
|
+
* `c.define`" — that turns it into an unregistered module, i.e. straight back
|
|
1641
|
+
* to here.
|
|
1630
1642
|
*/
|
|
1631
1643
|
function createMissingModuleDiagnostic({
|
|
1632
|
-
moduleFilePath
|
|
1644
|
+
moduleFilePath,
|
|
1645
|
+
range
|
|
1633
1646
|
}) {
|
|
1634
|
-
return build(
|
|
1647
|
+
return build(range, `${moduleFilePath} is not registered in val.modules, so Val will not serve it. Add it there — or, if this file is not meant to be a Val module, export what it holds by name instead: a default export is what makes a *.val.ts a module.`, {
|
|
1635
1648
|
code: "val/missing-module",
|
|
1636
1649
|
sourcePath: moduleFilePath
|
|
1637
1650
|
});
|
|
@@ -2243,6 +2256,18 @@ function findRegisteredModuleSpecifiers(sourceFile) {
|
|
|
2243
2256
|
/**
|
|
2244
2257
|
* Whether `moduleFilePath` is registered by the given `val.modules` file.
|
|
2245
2258
|
*
|
|
2259
|
+
* Answers `true` whenever it cannot tell, which is the same safe direction
|
|
2260
|
+
* {@link findRegisteredModuleSpecifiers} takes and matters more here than it
|
|
2261
|
+
* looks: the caller publishes the missing-module diagnostic *instead of* the
|
|
2262
|
+
* module's own diagnostics, so a false "unregistered" does not just add noise,
|
|
2263
|
+
* it hides every real finding in the file. Two things it cannot tell:
|
|
2264
|
+
*
|
|
2265
|
+
* - a `val.modules` with no dynamic imports at all, which is what a registry
|
|
2266
|
+
* that builds its list somewhere else looks like from here;
|
|
2267
|
+
* - a non-relative specifier naming a `.val` module (`_/content/page.val`),
|
|
2268
|
+
* which is a tsconfig path alias — a shape `loadValModules` resolves and
|
|
2269
|
+
* this file, having no compiler options, cannot.
|
|
2270
|
+
*
|
|
2246
2271
|
* @param valModulesDir directory containing the val.modules file, relative to
|
|
2247
2272
|
* the Val root (`""` when it sits at the root).
|
|
2248
2273
|
* @param moduleFilePath the module's path, Val-style: root-relative, leading
|
|
@@ -2254,11 +2279,23 @@ function isModuleRegistered({
|
|
|
2254
2279
|
moduleFilePath
|
|
2255
2280
|
}) {
|
|
2256
2281
|
const target = stripValModuleExtension(moduleFilePath);
|
|
2257
|
-
|
|
2282
|
+
const specifiers = findRegisteredModuleSpecifiers(sourceFile);
|
|
2283
|
+
if (specifiers.length === 0) {
|
|
2284
|
+
return true;
|
|
2285
|
+
}
|
|
2286
|
+
return specifiers.some(specifier => {
|
|
2287
|
+
const stripped = stripValModuleExtension(specifier);
|
|
2258
2288
|
// Specifiers are written relative to the val.modules file and normally omit
|
|
2259
2289
|
// the extension ("./content/page.val").
|
|
2260
|
-
|
|
2261
|
-
|
|
2290
|
+
if (specifier.startsWith(".")) {
|
|
2291
|
+
return path__default["default"].posix.normalize(path__default["default"].posix.join("/", valModulesDir, stripped)) === target;
|
|
2292
|
+
}
|
|
2293
|
+
if (specifier.startsWith("/")) {
|
|
2294
|
+
return stripped === target;
|
|
2295
|
+
}
|
|
2296
|
+
// A path alias, which needs the project's tsconfig to resolve. A bare
|
|
2297
|
+
// package import is not one of these, so it stays a plain non-match.
|
|
2298
|
+
return stripped.endsWith(".val");
|
|
2262
2299
|
});
|
|
2263
2300
|
}
|
|
2264
2301
|
|
|
@@ -3668,6 +3705,60 @@ function keysOfKeyOf(schema, snapshot) {
|
|
|
3668
3705
|
}
|
|
3669
3706
|
}
|
|
3670
3707
|
|
|
3708
|
+
/**
|
|
3709
|
+
* Where a file declares its Val module, or `undefined` when it declares none.
|
|
3710
|
+
*
|
|
3711
|
+
* `*.val.ts` is a naming convention, not a guarantee: a file under it may hold
|
|
3712
|
+
* nothing but the schemas and helpers the modules beside it import. The default
|
|
3713
|
+
* export is what separates the two, and `findDefaultExport` in
|
|
3714
|
+
* `@valbuild/server` is the one implementation of that rule — the same one
|
|
3715
|
+
* `val validate` uses to decide whether an unregistered file is worth reporting
|
|
3716
|
+
* at all. It is imported rather than reimplemented because the subtleties are
|
|
3717
|
+
* not obvious (a star re-export carries no default, a type-only export is gone
|
|
3718
|
+
* after transpilation), and two copies would answer differently the first time
|
|
3719
|
+
* one of them was fixed.
|
|
3720
|
+
*
|
|
3721
|
+
* Only the `export default` keywords are returned, never the whole definition:
|
|
3722
|
+
* a module's source is most of its file, and a diagnostic spanning that paints
|
|
3723
|
+
* the file red.
|
|
3724
|
+
*/
|
|
3725
|
+
function findValModuleDefinition(sourceFile) {
|
|
3726
|
+
const statement = server.findDefaultExport(sourceFile);
|
|
3727
|
+
if (!statement) {
|
|
3728
|
+
return undefined;
|
|
3729
|
+
}
|
|
3730
|
+
const start = statement.getStart(sourceFile);
|
|
3731
|
+
return {
|
|
3732
|
+
start: sourceFile.getLineAndCharacterOfPosition(start),
|
|
3733
|
+
end: sourceFile.getLineAndCharacterOfPosition(keywordsEnd(sourceFile, statement, start))
|
|
3734
|
+
};
|
|
3735
|
+
}
|
|
3736
|
+
|
|
3737
|
+
/**
|
|
3738
|
+
* The end of the `export default` a statement opens with.
|
|
3739
|
+
*
|
|
3740
|
+
* Three shapes reach this, and only the first two can be narrowed: `export
|
|
3741
|
+
* default <expr>`, where everything before the expression is the keywords;
|
|
3742
|
+
* `export default function f() {}` and `export default class C {}`, where they
|
|
3743
|
+
* are modifiers; and `export { x as default }`, which has no `default` keyword
|
|
3744
|
+
* to stop at and is short enough to underline whole.
|
|
3745
|
+
*/
|
|
3746
|
+
function keywordsEnd(sourceFile, statement, start) {
|
|
3747
|
+
if (ts__default["default"].isExportAssignment(statement)) {
|
|
3748
|
+
// Up to the exported expression is `export default` plus whitespace (and,
|
|
3749
|
+
// rarely, a comment); trimming the tail keeps the underline on the keywords.
|
|
3750
|
+
return start + sourceFile.text.slice(start, statement.expression.getStart(sourceFile)).trimEnd().length;
|
|
3751
|
+
}
|
|
3752
|
+
const defaultModifier = ts__default["default"].canHaveModifiers(statement) ? (ts__default["default"].getModifiers(statement) ?? []).find(modifier => modifier.kind === ts__default["default"].SyntaxKind.DefaultKeyword) : undefined;
|
|
3753
|
+
return defaultModifier?.end ?? statement.end;
|
|
3754
|
+
}
|
|
3755
|
+
|
|
3756
|
+
/** Parse a Val module's text the way {@link findValModuleDefinition} needs it. */
|
|
3757
|
+
function parseValModule(moduleFilePath, text) {
|
|
3758
|
+
// Parent pointers: `getStart` walks them to skip leading trivia.
|
|
3759
|
+
return ts__default["default"].createSourceFile(moduleFilePath, text, ts__default["default"].ScriptTarget.ES2020, true);
|
|
3760
|
+
}
|
|
3761
|
+
|
|
3671
3762
|
/**
|
|
3672
3763
|
* The two remedies for a gallery-backed field pointing at something its gallery
|
|
3673
3764
|
* does not have.
|
|
@@ -4127,15 +4218,14 @@ function applyEnvOverrides(options) {
|
|
|
4127
4218
|
const PROJECT_WIDE_FILE_RE = /[/\\]val\.(modules|config)\.(ts|js)$/;
|
|
4128
4219
|
|
|
4129
4220
|
/**
|
|
4130
|
-
*
|
|
4221
|
+
* Whether `val.modules` leaves this module out.
|
|
4131
4222
|
*
|
|
4132
4223
|
* Reads `val.modules.{ts,js}` on demand, through the editor's buffer when it is
|
|
4133
4224
|
* open: adding a module to the registry must clear the diagnostic straight away,
|
|
4134
|
-
* not only once the user saves.
|
|
4135
|
-
*
|
|
4136
|
-
* module.
|
|
4225
|
+
* not only once the user saves. Answers `false` when no such file exists — that
|
|
4226
|
+
* is a project-level problem, not something to blame on an individual module.
|
|
4137
4227
|
*/
|
|
4138
|
-
function
|
|
4228
|
+
function isModuleUnregistered(valRoot, moduleFilePath, read) {
|
|
4139
4229
|
for (const candidate of ["val.modules.ts", "val.modules.js"]) {
|
|
4140
4230
|
const file = path__default["default"].join(valRoot, candidate);
|
|
4141
4231
|
let text = read(file);
|
|
@@ -4146,16 +4236,13 @@ function findMissingModuleDiagnostic(valRoot, moduleFilePath, read) {
|
|
|
4146
4236
|
continue;
|
|
4147
4237
|
}
|
|
4148
4238
|
}
|
|
4149
|
-
|
|
4239
|
+
return !isModuleRegistered({
|
|
4150
4240
|
sourceFile: ts__default["default"].createSourceFile(file, text, ts__default["default"].ScriptTarget.ES2020),
|
|
4151
4241
|
valModulesDir: "",
|
|
4152
4242
|
moduleFilePath
|
|
4153
4243
|
});
|
|
4154
|
-
return registered ? undefined : createMissingModuleDiagnostic({
|
|
4155
|
-
moduleFilePath
|
|
4156
|
-
});
|
|
4157
4244
|
}
|
|
4158
|
-
return
|
|
4245
|
+
return false;
|
|
4159
4246
|
}
|
|
4160
4247
|
|
|
4161
4248
|
/**
|
|
@@ -4220,6 +4307,27 @@ function createValLanguageServer(connection) {
|
|
|
4220
4307
|
return;
|
|
4221
4308
|
}
|
|
4222
4309
|
try {
|
|
4310
|
+
// An unregistered file is answered from its own text alone, before the
|
|
4311
|
+
// project is consulted at all. Val cannot have evaluated it -- it is not
|
|
4312
|
+
// in the graph `val.modules` describes -- so everything downstream has
|
|
4313
|
+
// only the one thing to say about it, and said it twice: `Service.get`
|
|
4314
|
+
// reports `Module '...' was not found in val.modules` as a fatal on line
|
|
4315
|
+
// 1, beside this diagnostic saying the same. And when the file has no
|
|
4316
|
+
// `export default` neither of them is a finding at all: it is a
|
|
4317
|
+
// `*.val.ts` holding the schemas the modules beside it import, which Val
|
|
4318
|
+
// never serves and there is nothing to register.
|
|
4319
|
+
const unregistered = isModuleUnregistered(project.valRoot, moduleFilePath, readOpenDocument);
|
|
4320
|
+
if (unregistered) {
|
|
4321
|
+
const definition = findValModuleDefinition(parseValModule(moduleFilePath, document.getText()));
|
|
4322
|
+
connection.sendDiagnostics({
|
|
4323
|
+
uri,
|
|
4324
|
+
diagnostics: definition ? [createMissingModuleDiagnostic({
|
|
4325
|
+
moduleFilePath,
|
|
4326
|
+
range: definition
|
|
4327
|
+
})] : []
|
|
4328
|
+
});
|
|
4329
|
+
return;
|
|
4330
|
+
}
|
|
4223
4331
|
// The module's own content changed, so any cached result is stale.
|
|
4224
4332
|
project.invalidate(moduleFilePath);
|
|
4225
4333
|
const result = await project.getModule(moduleFilePath);
|
|
@@ -4283,10 +4391,6 @@ function createValLanguageServer(connection) {
|
|
|
4283
4391
|
mediaChecks
|
|
4284
4392
|
} : {})
|
|
4285
4393
|
});
|
|
4286
|
-
const unregistered = findMissingModuleDiagnostic(project.valRoot, moduleFilePath, readOpenDocument);
|
|
4287
|
-
if (unregistered) {
|
|
4288
|
-
diagnostics.push(unregistered);
|
|
4289
|
-
}
|
|
4290
4394
|
connection.sendDiagnostics({
|
|
4291
4395
|
uri,
|
|
4292
4396
|
diagnostics
|
|
@@ -2,7 +2,7 @@ import fs from 'fs';
|
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import ts from 'typescript';
|
|
4
4
|
import { Internal, DEFAULT_VAL_REMOTE_HOST, DEFAULT_CONTENT_HOST } from '@valbuild/core';
|
|
5
|
-
import { fixHandlers, createService, analyzeValModule, createFixPatch, parsePersonalAccessTokenFile, getPersonalAccessTokenPath, startValLogin, awaitValLoginConfirmation, persistPersonalAccessToken, ValLoginError, findAndEvalValConfigFile, patchSourceFile, getSettings, uploadRemoteFile, findJsonEntryFilePath, rebaseContentOp, classifyJsonValuesOp, planJsonValuesEntryExtraction, extractImageMetadata, extractFileMetadata } from '@valbuild/server';
|
|
5
|
+
import { fixHandlers, createService, analyzeValModule, createFixPatch, parsePersonalAccessTokenFile, getPersonalAccessTokenPath, startValLogin, awaitValLoginConfirmation, persistPersonalAccessToken, ValLoginError, findAndEvalValConfigFile, patchSourceFile, getSettings, uploadRemoteFile, findJsonEntryFilePath, rebaseContentOp, classifyJsonValuesOp, planJsonValuesEntryExtraction, extractImageMetadata, extractFileMetadata, findDefaultExport } from '@valbuild/server';
|
|
6
6
|
import { DiagnosticSeverity, ShowDocumentRequest, ApplyWorkspaceEditRequest, CodeActionKind, CodeAction, MarkupContent, CompletionItemKind, DidChangeWatchedFilesNotification } from 'vscode-languageserver';
|
|
7
7
|
import { TextDocuments, TextDocumentSyncKind, createConnection, ProposedFeatures } from 'vscode-languageserver/node';
|
|
8
8
|
import { TextDocument } from 'vscode-languageserver-textdocument';
|
|
@@ -172,7 +172,7 @@ var packageJson = {
|
|
|
172
172
|
"lsp",
|
|
173
173
|
"language-server"
|
|
174
174
|
],
|
|
175
|
-
version: "0.
|
|
175
|
+
version: "0.136.1",
|
|
176
176
|
bin: {
|
|
177
177
|
"val-language-server": "./bin.js"
|
|
178
178
|
},
|
|
@@ -1616,11 +1616,24 @@ function createProjectErrorDiagnostic({
|
|
|
1616
1616
|
*
|
|
1617
1617
|
* Val only serves modules listed there, so an unregistered module silently does
|
|
1618
1618
|
* nothing — worth surfacing even though it is not a validation error.
|
|
1619
|
+
*
|
|
1620
|
+
* Reported on the `export default`, which is also why a file that has none gets
|
|
1621
|
+
* no diagnostic at all (see `findValModuleDefinition`): the default export is
|
|
1622
|
+
* the thing that makes a `*.val.ts` a module, so it is the thing being asked
|
|
1623
|
+
* about, and pointing at line 1 of a file full of shared schemas asked its
|
|
1624
|
+
* author to register something that does not exist.
|
|
1625
|
+
*
|
|
1626
|
+
* The message carries both remedies for the same reason. Only one of them is
|
|
1627
|
+
* "add it to val.modules"; the other is to stop default-exporting, which is how
|
|
1628
|
+
* a helper that landed in the module slot is fixed. Never "wrap it in
|
|
1629
|
+
* `c.define`" — that turns it into an unregistered module, i.e. straight back
|
|
1630
|
+
* to here.
|
|
1619
1631
|
*/
|
|
1620
1632
|
function createMissingModuleDiagnostic({
|
|
1621
|
-
moduleFilePath
|
|
1633
|
+
moduleFilePath,
|
|
1634
|
+
range
|
|
1622
1635
|
}) {
|
|
1623
|
-
return build(
|
|
1636
|
+
return build(range, `${moduleFilePath} is not registered in val.modules, so Val will not serve it. Add it there — or, if this file is not meant to be a Val module, export what it holds by name instead: a default export is what makes a *.val.ts a module.`, {
|
|
1624
1637
|
code: "val/missing-module",
|
|
1625
1638
|
sourcePath: moduleFilePath
|
|
1626
1639
|
});
|
|
@@ -2232,6 +2245,18 @@ function findRegisteredModuleSpecifiers(sourceFile) {
|
|
|
2232
2245
|
/**
|
|
2233
2246
|
* Whether `moduleFilePath` is registered by the given `val.modules` file.
|
|
2234
2247
|
*
|
|
2248
|
+
* Answers `true` whenever it cannot tell, which is the same safe direction
|
|
2249
|
+
* {@link findRegisteredModuleSpecifiers} takes and matters more here than it
|
|
2250
|
+
* looks: the caller publishes the missing-module diagnostic *instead of* the
|
|
2251
|
+
* module's own diagnostics, so a false "unregistered" does not just add noise,
|
|
2252
|
+
* it hides every real finding in the file. Two things it cannot tell:
|
|
2253
|
+
*
|
|
2254
|
+
* - a `val.modules` with no dynamic imports at all, which is what a registry
|
|
2255
|
+
* that builds its list somewhere else looks like from here;
|
|
2256
|
+
* - a non-relative specifier naming a `.val` module (`_/content/page.val`),
|
|
2257
|
+
* which is a tsconfig path alias — a shape `loadValModules` resolves and
|
|
2258
|
+
* this file, having no compiler options, cannot.
|
|
2259
|
+
*
|
|
2235
2260
|
* @param valModulesDir directory containing the val.modules file, relative to
|
|
2236
2261
|
* the Val root (`""` when it sits at the root).
|
|
2237
2262
|
* @param moduleFilePath the module's path, Val-style: root-relative, leading
|
|
@@ -2243,11 +2268,23 @@ function isModuleRegistered({
|
|
|
2243
2268
|
moduleFilePath
|
|
2244
2269
|
}) {
|
|
2245
2270
|
const target = stripValModuleExtension(moduleFilePath);
|
|
2246
|
-
|
|
2271
|
+
const specifiers = findRegisteredModuleSpecifiers(sourceFile);
|
|
2272
|
+
if (specifiers.length === 0) {
|
|
2273
|
+
return true;
|
|
2274
|
+
}
|
|
2275
|
+
return specifiers.some(specifier => {
|
|
2276
|
+
const stripped = stripValModuleExtension(specifier);
|
|
2247
2277
|
// Specifiers are written relative to the val.modules file and normally omit
|
|
2248
2278
|
// the extension ("./content/page.val").
|
|
2249
|
-
|
|
2250
|
-
|
|
2279
|
+
if (specifier.startsWith(".")) {
|
|
2280
|
+
return path.posix.normalize(path.posix.join("/", valModulesDir, stripped)) === target;
|
|
2281
|
+
}
|
|
2282
|
+
if (specifier.startsWith("/")) {
|
|
2283
|
+
return stripped === target;
|
|
2284
|
+
}
|
|
2285
|
+
// A path alias, which needs the project's tsconfig to resolve. A bare
|
|
2286
|
+
// package import is not one of these, so it stays a plain non-match.
|
|
2287
|
+
return stripped.endsWith(".val");
|
|
2251
2288
|
});
|
|
2252
2289
|
}
|
|
2253
2290
|
|
|
@@ -3657,6 +3694,60 @@ function keysOfKeyOf(schema, snapshot) {
|
|
|
3657
3694
|
}
|
|
3658
3695
|
}
|
|
3659
3696
|
|
|
3697
|
+
/**
|
|
3698
|
+
* Where a file declares its Val module, or `undefined` when it declares none.
|
|
3699
|
+
*
|
|
3700
|
+
* `*.val.ts` is a naming convention, not a guarantee: a file under it may hold
|
|
3701
|
+
* nothing but the schemas and helpers the modules beside it import. The default
|
|
3702
|
+
* export is what separates the two, and `findDefaultExport` in
|
|
3703
|
+
* `@valbuild/server` is the one implementation of that rule — the same one
|
|
3704
|
+
* `val validate` uses to decide whether an unregistered file is worth reporting
|
|
3705
|
+
* at all. It is imported rather than reimplemented because the subtleties are
|
|
3706
|
+
* not obvious (a star re-export carries no default, a type-only export is gone
|
|
3707
|
+
* after transpilation), and two copies would answer differently the first time
|
|
3708
|
+
* one of them was fixed.
|
|
3709
|
+
*
|
|
3710
|
+
* Only the `export default` keywords are returned, never the whole definition:
|
|
3711
|
+
* a module's source is most of its file, and a diagnostic spanning that paints
|
|
3712
|
+
* the file red.
|
|
3713
|
+
*/
|
|
3714
|
+
function findValModuleDefinition(sourceFile) {
|
|
3715
|
+
const statement = findDefaultExport(sourceFile);
|
|
3716
|
+
if (!statement) {
|
|
3717
|
+
return undefined;
|
|
3718
|
+
}
|
|
3719
|
+
const start = statement.getStart(sourceFile);
|
|
3720
|
+
return {
|
|
3721
|
+
start: sourceFile.getLineAndCharacterOfPosition(start),
|
|
3722
|
+
end: sourceFile.getLineAndCharacterOfPosition(keywordsEnd(sourceFile, statement, start))
|
|
3723
|
+
};
|
|
3724
|
+
}
|
|
3725
|
+
|
|
3726
|
+
/**
|
|
3727
|
+
* The end of the `export default` a statement opens with.
|
|
3728
|
+
*
|
|
3729
|
+
* Three shapes reach this, and only the first two can be narrowed: `export
|
|
3730
|
+
* default <expr>`, where everything before the expression is the keywords;
|
|
3731
|
+
* `export default function f() {}` and `export default class C {}`, where they
|
|
3732
|
+
* are modifiers; and `export { x as default }`, which has no `default` keyword
|
|
3733
|
+
* to stop at and is short enough to underline whole.
|
|
3734
|
+
*/
|
|
3735
|
+
function keywordsEnd(sourceFile, statement, start) {
|
|
3736
|
+
if (ts.isExportAssignment(statement)) {
|
|
3737
|
+
// Up to the exported expression is `export default` plus whitespace (and,
|
|
3738
|
+
// rarely, a comment); trimming the tail keeps the underline on the keywords.
|
|
3739
|
+
return start + sourceFile.text.slice(start, statement.expression.getStart(sourceFile)).trimEnd().length;
|
|
3740
|
+
}
|
|
3741
|
+
const defaultModifier = ts.canHaveModifiers(statement) ? (ts.getModifiers(statement) ?? []).find(modifier => modifier.kind === ts.SyntaxKind.DefaultKeyword) : undefined;
|
|
3742
|
+
return defaultModifier?.end ?? statement.end;
|
|
3743
|
+
}
|
|
3744
|
+
|
|
3745
|
+
/** Parse a Val module's text the way {@link findValModuleDefinition} needs it. */
|
|
3746
|
+
function parseValModule(moduleFilePath, text) {
|
|
3747
|
+
// Parent pointers: `getStart` walks them to skip leading trivia.
|
|
3748
|
+
return ts.createSourceFile(moduleFilePath, text, ts.ScriptTarget.ES2020, true);
|
|
3749
|
+
}
|
|
3750
|
+
|
|
3660
3751
|
/**
|
|
3661
3752
|
* The two remedies for a gallery-backed field pointing at something its gallery
|
|
3662
3753
|
* does not have.
|
|
@@ -4116,15 +4207,14 @@ function applyEnvOverrides(options) {
|
|
|
4116
4207
|
const PROJECT_WIDE_FILE_RE = /[/\\]val\.(modules|config)\.(ts|js)$/;
|
|
4117
4208
|
|
|
4118
4209
|
/**
|
|
4119
|
-
*
|
|
4210
|
+
* Whether `val.modules` leaves this module out.
|
|
4120
4211
|
*
|
|
4121
4212
|
* Reads `val.modules.{ts,js}` on demand, through the editor's buffer when it is
|
|
4122
4213
|
* open: adding a module to the registry must clear the diagnostic straight away,
|
|
4123
|
-
* not only once the user saves.
|
|
4124
|
-
*
|
|
4125
|
-
* module.
|
|
4214
|
+
* not only once the user saves. Answers `false` when no such file exists — that
|
|
4215
|
+
* is a project-level problem, not something to blame on an individual module.
|
|
4126
4216
|
*/
|
|
4127
|
-
function
|
|
4217
|
+
function isModuleUnregistered(valRoot, moduleFilePath, read) {
|
|
4128
4218
|
for (const candidate of ["val.modules.ts", "val.modules.js"]) {
|
|
4129
4219
|
const file = path.join(valRoot, candidate);
|
|
4130
4220
|
let text = read(file);
|
|
@@ -4135,16 +4225,13 @@ function findMissingModuleDiagnostic(valRoot, moduleFilePath, read) {
|
|
|
4135
4225
|
continue;
|
|
4136
4226
|
}
|
|
4137
4227
|
}
|
|
4138
|
-
|
|
4228
|
+
return !isModuleRegistered({
|
|
4139
4229
|
sourceFile: ts.createSourceFile(file, text, ts.ScriptTarget.ES2020),
|
|
4140
4230
|
valModulesDir: "",
|
|
4141
4231
|
moduleFilePath
|
|
4142
4232
|
});
|
|
4143
|
-
return registered ? undefined : createMissingModuleDiagnostic({
|
|
4144
|
-
moduleFilePath
|
|
4145
|
-
});
|
|
4146
4233
|
}
|
|
4147
|
-
return
|
|
4234
|
+
return false;
|
|
4148
4235
|
}
|
|
4149
4236
|
|
|
4150
4237
|
/**
|
|
@@ -4209,6 +4296,27 @@ function createValLanguageServer(connection) {
|
|
|
4209
4296
|
return;
|
|
4210
4297
|
}
|
|
4211
4298
|
try {
|
|
4299
|
+
// An unregistered file is answered from its own text alone, before the
|
|
4300
|
+
// project is consulted at all. Val cannot have evaluated it -- it is not
|
|
4301
|
+
// in the graph `val.modules` describes -- so everything downstream has
|
|
4302
|
+
// only the one thing to say about it, and said it twice: `Service.get`
|
|
4303
|
+
// reports `Module '...' was not found in val.modules` as a fatal on line
|
|
4304
|
+
// 1, beside this diagnostic saying the same. And when the file has no
|
|
4305
|
+
// `export default` neither of them is a finding at all: it is a
|
|
4306
|
+
// `*.val.ts` holding the schemas the modules beside it import, which Val
|
|
4307
|
+
// never serves and there is nothing to register.
|
|
4308
|
+
const unregistered = isModuleUnregistered(project.valRoot, moduleFilePath, readOpenDocument);
|
|
4309
|
+
if (unregistered) {
|
|
4310
|
+
const definition = findValModuleDefinition(parseValModule(moduleFilePath, document.getText()));
|
|
4311
|
+
connection.sendDiagnostics({
|
|
4312
|
+
uri,
|
|
4313
|
+
diagnostics: definition ? [createMissingModuleDiagnostic({
|
|
4314
|
+
moduleFilePath,
|
|
4315
|
+
range: definition
|
|
4316
|
+
})] : []
|
|
4317
|
+
});
|
|
4318
|
+
return;
|
|
4319
|
+
}
|
|
4212
4320
|
// The module's own content changed, so any cached result is stale.
|
|
4213
4321
|
project.invalidate(moduleFilePath);
|
|
4214
4322
|
const result = await project.getModule(moduleFilePath);
|
|
@@ -4272,10 +4380,6 @@ function createValLanguageServer(connection) {
|
|
|
4272
4380
|
mediaChecks
|
|
4273
4381
|
} : {})
|
|
4274
4382
|
});
|
|
4275
|
-
const unregistered = findMissingModuleDiagnostic(project.valRoot, moduleFilePath, readOpenDocument);
|
|
4276
|
-
if (unregistered) {
|
|
4277
|
-
diagnostics.push(unregistered);
|
|
4278
|
-
}
|
|
4279
4383
|
connection.sendDiagnostics({
|
|
4280
4384
|
uri,
|
|
4281
4385
|
diagnostics
|
package/package.json
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"lsp",
|
|
12
12
|
"language-server"
|
|
13
13
|
],
|
|
14
|
-
"version": "0.
|
|
14
|
+
"version": "0.136.1",
|
|
15
15
|
"bin": {
|
|
16
16
|
"val-language-server": "./bin.js"
|
|
17
17
|
},
|
|
@@ -29,9 +29,9 @@
|
|
|
29
29
|
"typescript": "^6.0.3",
|
|
30
30
|
"vscode-languageserver": "^10.1.0",
|
|
31
31
|
"vscode-languageserver-textdocument": "^1.0.14",
|
|
32
|
-
"@valbuild/
|
|
33
|
-
"@valbuild/
|
|
34
|
-
"@valbuild/
|
|
32
|
+
"@valbuild/core": "0.136.1",
|
|
33
|
+
"@valbuild/server": "0.136.1",
|
|
34
|
+
"@valbuild/shared": "0.136.1"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@types/jest": "^30.0.0",
|