@tamagui/language-service 3.0.0-beta.924.1 → 3.0.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.
Files changed (63) hide show
  1. package/README.md +25 -134
  2. package/dist/cjs/check.cjs +85 -10
  3. package/dist/cjs/check.native.cjs +138 -22
  4. package/dist/cjs/check.native.js +138 -22
  5. package/dist/cjs/check.native.js.map +1 -1
  6. package/dist/cjs/core.cjs +8 -4
  7. package/dist/cjs/core.native.cjs +10 -9
  8. package/dist/cjs/core.native.js +10 -9
  9. package/dist/cjs/core.native.js.map +1 -1
  10. package/dist/cjs/document.cjs +4 -3
  11. package/dist/cjs/document.native.cjs +5 -3
  12. package/dist/cjs/document.native.js +5 -3
  13. package/dist/cjs/document.native.js.map +1 -1
  14. package/dist/cjs/extract-estree.native.cjs +2 -6
  15. package/dist/cjs/extract-estree.native.js +2 -6
  16. package/dist/cjs/extract-estree.native.js.map +1 -1
  17. package/dist/cjs/index.cjs +5 -284
  18. package/dist/cjs/index.native.cjs +5 -395
  19. package/dist/cjs/index.native.js +5 -395
  20. package/dist/cjs/index.native.js.map +1 -1
  21. package/dist/esm/check.mjs +87 -12
  22. package/dist/esm/check.mjs.map +1 -1
  23. package/dist/esm/check.native.js +140 -24
  24. package/dist/esm/check.native.js.map +1 -1
  25. package/dist/esm/core.mjs +8 -4
  26. package/dist/esm/core.mjs.map +1 -1
  27. package/dist/esm/core.native.js +10 -9
  28. package/dist/esm/core.native.js.map +1 -1
  29. package/dist/esm/document.mjs +4 -3
  30. package/dist/esm/document.mjs.map +1 -1
  31. package/dist/esm/document.native.js +5 -3
  32. package/dist/esm/document.native.js.map +1 -1
  33. package/dist/esm/extract-estree.native.js +2 -6
  34. package/dist/esm/extract-estree.native.js.map +1 -1
  35. package/dist/esm/index.mjs +5 -279
  36. package/dist/esm/index.native.js +5 -390
  37. package/package.json +8 -17
  38. package/src/check.ts +118 -13
  39. package/src/core.ts +25 -6
  40. package/src/document.ts +21 -4
  41. package/src/index.ts +4 -452
  42. package/types/check.d.ts +4 -0
  43. package/types/check.d.ts.map +2 -2
  44. package/types/core.d.ts +7 -2
  45. package/types/core.d.ts.map +2 -2
  46. package/types/document.d.ts +8 -2
  47. package/types/document.d.ts.map +2 -2
  48. package/types/index.d.ts +4 -7
  49. package/types/index.d.ts.map +2 -2
  50. package/dist/cjs/host.cjs +0 -37
  51. package/dist/cjs/host.native.cjs +0 -60
  52. package/dist/cjs/host.native.js +0 -62
  53. package/dist/cjs/host.native.js.map +0 -1
  54. package/dist/esm/host.mjs +0 -17
  55. package/dist/esm/host.mjs.map +0 -1
  56. package/dist/esm/host.native.js +0 -38
  57. package/dist/esm/host.native.js.map +0 -1
  58. package/dist/esm/index.mjs.map +0 -1
  59. package/dist/esm/index.native.js.map +0 -1
  60. package/index.cjs +0 -1
  61. package/src/host.ts +0 -32
  62. package/types/host.d.ts +0 -10
  63. package/types/host.d.ts.map +0 -11
@@ -1,392 +1,7 @@
1
- import { isAbsolute, resolve } from "path";
2
- import { completionSortText, createStyleTooling } from "./core.native.js";
3
- import { resolveTamaguiHost } from "./host.native.js";
1
+ export * from "./core.native.js"
4
2
 
5
- var completionSource = "@tamagui/language-service";
6
- var diagnosticCode = 78711;
7
- var defaultConfigPath = ".tamagui/tamagui.config.json";
8
- var flatStyledModules = /* @__PURE__ */ new Set([
9
- "tamagui",
10
- "tamagui/unstyled",
11
- "@tamagui/core",
12
- "@tamagui/ui",
13
- "@tamagui/web"
14
- ]);
15
- function loadTooling(info, configPath) {
16
- var contents = info.serverHost.readFile(configPath);
17
- if (contents === void 0) return null;
18
- try {
19
- return createStyleTooling(JSON.parse(contents));
20
- } catch (e) {
21
- return null;
22
- }
23
- }
24
- function findStringLiteralAtPosition(typescript, sourceFile, position) {
25
- var found = null;
26
- var visit = function(node) {
27
- if (position < node.getFullStart() || position > node.getEnd()) return;
28
- if (typescript.isStringLiteralLike(node) && position >= node.getStart(sourceFile) + 1 && position <= node.getEnd() - 1) {
29
- found = node;
30
- return;
31
- }
32
- typescript.forEachChild(node, visit);
33
- };
34
- visit(sourceFile);
35
- return found;
36
- }
37
- function importedBindings(typescript, sourceFile) {
38
- var styled = /* @__PURE__ */ new Set();
39
- var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = void 0;
40
- try {
41
- for (var _iterator = sourceFile.statements[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
42
- var statement = _step.value;
43
- var _statement_importClause;
44
- if (!typescript.isImportDeclaration(statement) || !typescript.isStringLiteral(statement.moduleSpecifier)) {
45
- continue;
46
- }
47
- var source = statement.moduleSpecifier.text;
48
- if (!flatStyledModules.has(source)) continue;
49
- var bindings = (_statement_importClause = statement.importClause) === null || _statement_importClause === void 0 ? void 0 : _statement_importClause.namedBindings;
50
- if (!bindings) continue;
51
- if (typescript.isNamespaceImport(bindings)) continue;
52
- var _iteratorNormalCompletion1 = true, _didIteratorError1 = false, _iteratorError1 = void 0;
53
- try {
54
- for (var _iterator1 = bindings.elements[Symbol.iterator](), _step1; !(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done); _iteratorNormalCompletion1 = true) {
55
- var element = _step1.value;
56
- var _element_propertyName;
57
- var imported = ((_element_propertyName = element.propertyName) === null || _element_propertyName === void 0 ? void 0 : _element_propertyName.text) || element.name.text;
58
- if (imported === "styled") styled.add(element.name.text);
59
- }
60
- } catch (err) {
61
- _didIteratorError1 = true;
62
- _iteratorError1 = err;
63
- } finally {
64
- try {
65
- if (!_iteratorNormalCompletion1 && _iterator1.return != null) {
66
- _iterator1.return();
67
- }
68
- } finally {
69
- if (_didIteratorError1) {
70
- throw _iteratorError1;
71
- }
72
- }
73
- }
74
- }
75
- } catch (err) {
76
- _didIteratorError = true;
77
- _iteratorError = err;
78
- } finally {
79
- try {
80
- if (!_iteratorNormalCompletion && _iterator.return != null) {
81
- _iterator.return();
82
- }
83
- } finally {
84
- if (_didIteratorError) {
85
- throw _iteratorError;
86
- }
87
- }
88
- }
89
- return { styled };
90
- }
91
- function propertyName(typescript, name) {
92
- if (typescript.isIdentifier(name) || typescript.isStringLiteral(name)) {
93
- return name.text;
94
- }
95
- return null;
96
- }
97
- function completionProperty(typescript, literal, tooling, checker, bindings) {
98
- var parent = literal.parent;
99
- if (typescript.isJsxExpression(parent)) parent = parent.parent;
100
- if (typescript.isJsxAttribute(parent)) {
101
- if (!typescript.isIdentifier(parent.name)) return null;
102
- var property = parent.name.text;
103
- if (!tooling.styleProps.has(property)) return null;
104
- var opening = parent.parent.parent;
105
- if (!typescript.isJsxOpeningLikeElement(opening)) return null;
106
- var host = resolveTamaguiHost(checker, opening.tagName);
107
- if (!(host === null || host === void 0 ? void 0 : host.accepts(property))) return null;
108
- return { property };
109
- }
110
- if (!typescript.isPropertyAssignment(parent)) return null;
111
- var property1 = propertyName(typescript, parent.name);
112
- if (!property1 || !tooling.styleProps.has(property1)) return null;
113
- var current = parent;
114
- while (current.parent && !typescript.isCallExpression(current.parent)) {
115
- current = current.parent;
116
- }
117
- if (!current.parent || !typescript.isCallExpression(current.parent)) return null;
118
- var call = current.parent;
119
- if (call.arguments[1] !== current || !typescript.isIdentifier(call.expression) || !bindings.styled.has(call.expression.text)) {
120
- return null;
121
- }
122
- var component = call.arguments[0];
123
- var host1 = component && resolveTamaguiHost(checker, component);
124
- if (!(host1 === null || host1 === void 0 ? void 0 : host1.accepts(property1))) return null;
125
- return { property: property1 };
126
- }
127
- function siteForLiteral(typescript, sourceFile, literal, tooling, checker, bindings) {
128
- var site = completionProperty(typescript, literal, tooling, checker, bindings);
129
- if (!site) return null;
130
- var contentStart = literal.getStart(sourceFile) + 1;
131
- var contentEnd = literal.getEnd() - 1;
132
- var sourceInput = sourceFile.text.slice(contentStart, contentEnd);
133
- if (literal.text !== sourceInput) return null;
134
- return {
135
- property: site.property,
136
- value: literal.text,
137
- contentStart,
138
- literal
139
- };
140
- }
141
- function collectLiteralSites(typescript, sourceFile, tooling, checker, bindings) {
142
- var sites = [];
143
- var visit = function(node) {
144
- if (typescript.isStringLiteralLike(node)) {
145
- var site = siteForLiteral(typescript, sourceFile, node, tooling, checker, bindings);
146
- if (site) sites.push(site);
147
- return;
148
- }
149
- typescript.forEachChild(node, visit);
150
- };
151
- visit(sourceFile);
152
- return sites;
153
- }
154
- function copyLanguageService(service) {
155
- var proxy = /* @__PURE__ */ Object.create(null);
156
- var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = void 0;
157
- try {
158
- for (var _iterator = Object.keys(service)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
159
- var key = _step.value;
160
- var value = service[key];
161
- proxy[key] = typeof value === "function" ? value.bind(service) : value;
162
- }
163
- } catch (err) {
164
- _didIteratorError = true;
165
- _iteratorError = err;
166
- } finally {
167
- try {
168
- if (!_iteratorNormalCompletion && _iterator.return != null) {
169
- _iterator.return();
170
- }
171
- } finally {
172
- if (_didIteratorError) {
173
- throw _iteratorError;
174
- }
175
- }
176
- }
177
- return proxy;
178
- }
179
- var init = function(param) {
180
- var { typescript } = param;
181
- return { create(info) {
182
- var pluginConfig = info.config;
183
- var configuredPath = (pluginConfig === null || pluginConfig === void 0 ? void 0 : pluginConfig.configPath) || defaultConfigPath;
184
- var configPath = isAbsolute(configuredPath) ? configuredPath : resolve(info.project.getCurrentDirectory(), configuredPath);
185
- var tooling = loadTooling(info, configPath);
186
- var watcher = info.serverHost.watchFile(configPath, function() {
187
- tooling = loadTooling(info, configPath);
188
- });
189
- var proxy = copyLanguageService(info.languageService);
190
- var baseCompletions = info.languageService.getCompletionsAtPosition.bind(info.languageService);
191
- var baseDetails = info.languageService.getCompletionEntryDetails.bind(info.languageService);
192
- var baseSemanticDiagnostics = info.languageService.getSemanticDiagnostics.bind(info.languageService);
193
- var baseQuickInfo = info.languageService.getQuickInfoAtPosition.bind(info.languageService);
194
- var baseDispose = info.languageService.dispose.bind(info.languageService);
195
- var bindingsBySourceFile = /* @__PURE__ */ new WeakMap();
196
- var fileContext = function(fileName) {
197
- var program = info.languageService.getProgram();
198
- var sourceFile = program === null || program === void 0 ? void 0 : program.getSourceFile(fileName);
199
- if (!program || !sourceFile) return null;
200
- var bindings = bindingsBySourceFile.get(sourceFile);
201
- if (!bindings) {
202
- bindings = importedBindings(typescript, sourceFile);
203
- bindingsBySourceFile.set(sourceFile, bindings);
204
- }
205
- return {
206
- sourceFile,
207
- checker: program.getTypeChecker(),
208
- bindings
209
- };
210
- };
211
- proxy.getCompletionsAtPosition = function(fileName, position, options, formattingSettings) {
212
- var getBaseCompletions = function() {
213
- return baseCompletions(fileName, position, options, formattingSettings);
214
- };
215
- if (!tooling) return getBaseCompletions();
216
- var context = fileContext(fileName);
217
- if (!context) return getBaseCompletions();
218
- var { sourceFile, checker, bindings } = context;
219
- var literal = findStringLiteralAtPosition(typescript, sourceFile, position);
220
- if (!literal) return getBaseCompletions();
221
- var site = siteForLiteral(typescript, sourceFile, literal, tooling, checker, bindings);
222
- if (!site) return getBaseCompletions();
223
- var cursorCompletions = tooling.completions(site.property, site.value, position - site.contentStart);
224
- if (!cursorCompletions) return getBaseCompletions();
225
- var replacementSpan = {
226
- start: site.contentStart + cursorCompletions.replaceStart,
227
- length: cursorCompletions.replaceLength
228
- };
229
- var entries = [];
230
- var contextualType;
231
- var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = void 0;
232
- try {
233
- for (var _iterator = cursorCompletions.completions[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
234
- var completion = _step.value;
235
- var insertText = completion.insertText || completion.value;
236
- var modifierKind = completion.kind === "modifier" ? tooling.modifierKind(completion.value) : void 0;
237
- if (completion.kind === "keyword") {
238
- contextualType || (contextualType = checker.getContextualType(literal));
239
- if (!contextualType || !checker.isTypeAssignableTo(checker.getStringLiteralType(insertText), contextualType)) {
240
- continue;
241
- }
242
- }
243
- entries.push({
244
- name: completion.value,
245
- kind: typescript.ScriptElementKind.string,
246
- kindModifiers: "",
247
- sortText: completionSortText(completion, modifierKind),
248
- insertText,
249
- replacementSpan,
250
- source: completionSource,
251
- labelDetails: { description: modifierKind ? `Tamagui ${modifierKind} modifier` : completion.kind === "configured" ? "Tamagui configured value" : "Tamagui style keyword" }
252
- });
253
- }
254
- } catch (err) {
255
- _didIteratorError = true;
256
- _iteratorError = err;
257
- } finally {
258
- try {
259
- if (!_iteratorNormalCompletion && _iterator.return != null) {
260
- _iterator.return();
261
- }
262
- } finally {
263
- if (_didIteratorError) {
264
- throw _iteratorError;
265
- }
266
- }
267
- }
268
- if (entries.length === 0) return getBaseCompletions();
269
- return {
270
- isGlobalCompletion: false,
271
- isMemberCompletion: false,
272
- isNewIdentifierLocation: true,
273
- isIncomplete: true,
274
- entries
275
- };
276
- };
277
- proxy.getCompletionEntryDetails = function(fileName, position, entryName, formatOptions, source, preferences, data) {
278
- if (source === completionSource) {
279
- return {
280
- name: entryName,
281
- kind: typescript.ScriptElementKind.string,
282
- kindModifiers: "",
283
- displayParts: [{
284
- text: entryName,
285
- kind: "stringLiteral"
286
- }],
287
- documentation: [{
288
- text: "Resolved from the active Tamagui config.",
289
- kind: "text"
290
- }],
291
- tags: []
292
- };
293
- }
294
- return baseDetails(fileName, position, entryName, formatOptions, source, preferences, data);
295
- };
296
- proxy.getSemanticDiagnostics = function(fileName) {
297
- var diagnostics = [...baseSemanticDiagnostics(fileName)];
298
- if (!tooling) return diagnostics;
299
- var context = fileContext(fileName);
300
- if (!context) return diagnostics;
301
- var { sourceFile, checker, bindings } = context;
302
- var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = void 0;
303
- try {
304
- for (var _iterator = collectLiteralSites(typescript, sourceFile, tooling, checker, bindings)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
305
- var site = _step.value;
306
- var _iteratorNormalCompletion1 = true, _didIteratorError1 = false, _iteratorError1 = void 0;
307
- try {
308
- for (var _iterator1 = tooling.diagnostics(site.property, site.value)[Symbol.iterator](), _step1; !(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done); _iteratorNormalCompletion1 = true) {
309
- var diagnostic = _step1.value;
310
- diagnostics.push({
311
- file: sourceFile,
312
- start: site.contentStart + diagnostic.start,
313
- length: Math.max(1, diagnostic.end - diagnostic.start),
314
- messageText: diagnostic.message,
315
- category: typescript.DiagnosticCategory.Error,
316
- code: diagnosticCode,
317
- source: completionSource
318
- });
319
- }
320
- } catch (err) {
321
- _didIteratorError1 = true;
322
- _iteratorError1 = err;
323
- } finally {
324
- try {
325
- if (!_iteratorNormalCompletion1 && _iterator1.return != null) {
326
- _iterator1.return();
327
- }
328
- } finally {
329
- if (_didIteratorError1) {
330
- throw _iteratorError1;
331
- }
332
- }
333
- }
334
- }
335
- } catch (err) {
336
- _didIteratorError = true;
337
- _iteratorError = err;
338
- } finally {
339
- try {
340
- if (!_iteratorNormalCompletion && _iterator.return != null) {
341
- _iterator.return();
342
- }
343
- } finally {
344
- if (_didIteratorError) {
345
- throw _iteratorError;
346
- }
347
- }
348
- }
349
- return diagnostics;
350
- };
351
- proxy.getQuickInfoAtPosition = function(fileName, position) {
352
- var getBaseQuickInfo = function() {
353
- return baseQuickInfo(fileName, position);
354
- };
355
- if (!tooling) return getBaseQuickInfo();
356
- var context = fileContext(fileName);
357
- if (!context) return getBaseQuickInfo();
358
- var { sourceFile, checker, bindings } = context;
359
- var literal = findStringLiteralAtPosition(typescript, sourceFile, position);
360
- if (!literal) return getBaseQuickInfo();
361
- var site = siteForLiteral(typescript, sourceFile, literal, tooling, checker, bindings);
362
- if (!site) return getBaseQuickInfo();
363
- var hover = tooling.hover(site.property, site.value, position - site.contentStart);
364
- if (!hover) return getBaseQuickInfo();
365
- return {
366
- kind: typescript.ScriptElementKind.string,
367
- kindModifiers: "",
368
- textSpan: {
369
- start: site.contentStart + hover.start,
370
- length: hover.end - hover.start
371
- },
372
- displayParts: [{
373
- text: hover.text,
374
- kind: "stringLiteral"
375
- }],
376
- documentation: [{
377
- text: hover.markdown,
378
- kind: "text"
379
- }]
380
- };
381
- };
382
- proxy.dispose = function() {
383
- watcher.close();
384
- baseDispose();
385
- };
386
- return proxy;
387
- } };
388
- };
389
- var index_default = init;
3
+ export * from "./document.native.js"
390
4
 
391
- export { index_default as default };
392
- //# sourceMappingURL=index.native.js.map
5
+ export * from "./extract-estree.native.js"
6
+
7
+ export * from "./extract-sucrase.native.js"
package/package.json CHANGED
@@ -1,26 +1,20 @@
1
1
  {
2
2
  "name": "@tamagui/language-service",
3
- "version": "3.0.0-beta.924.1",
3
+ "version": "3.0.1",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "sideEffects": false,
7
7
  "source": "src/index.ts",
8
8
  "files": [
9
- "index.cjs",
10
9
  "src",
11
10
  "types",
12
11
  "dist"
13
12
  ],
14
- "main": "index.cjs",
13
+ "main": "dist/cjs/index.cjs",
15
14
  "module": "dist/esm/index.mjs",
16
15
  "types": "types/index.d.ts",
17
16
  "exports": {
18
17
  "./package.json": "./package.json",
19
- "./host": {
20
- "types": "./types/host.d.ts",
21
- "import": "./dist/esm/host.mjs",
22
- "default": "./dist/esm/host.mjs"
23
- },
24
18
  "./core": {
25
19
  "types": "./types/core.d.ts",
26
20
  "import": "./dist/esm/core.mjs",
@@ -54,7 +48,7 @@
54
48
  ".": {
55
49
  "types": "./types/index.d.ts",
56
50
  "import": "./dist/esm/index.mjs",
57
- "require": "./index.cjs",
51
+ "require": "./dist/cjs/index.cjs",
58
52
  "default": "./dist/esm/index.mjs"
59
53
  }
60
54
  },
@@ -67,22 +61,19 @@
67
61
  "test:web": "bun run build && vitest --run test"
68
62
  },
69
63
  "dependencies": {
70
- "@tamagui/normalize-css-color": "3.0.0-beta.924.1",
71
- "@tamagui/style-grammar": "3.0.0-beta.924.1",
64
+ "@tamagui/normalize-css-color": "3.0.1",
65
+ "@tamagui/style-grammar": "3.0.1",
72
66
  "sucrase": "3.35.1"
73
67
  },
74
- "peerDependencies": {
75
- "typescript": ">=5.7"
76
- },
77
68
  "devDependencies": {
78
- "@tamagui/build": "3.0.0-beta.924.1",
69
+ "@tamagui/build": "3.0.1",
79
70
  "oxc-parser": "^0.112.0",
80
- "typescript": "~6.0.3",
81
71
  "vitest": "4.1.0"
82
72
  },
83
73
  "repository": {
84
74
  "type": "git",
85
75
  "url": "git+https://github.com/tamagui/tamagui.git",
86
76
  "directory": "code/core/language-service"
87
- }
77
+ },
78
+ "releaseSourceCommit": "c156b7cc55e5a6cc5883ebc8189720850ac32dbd"
88
79
  }
package/src/check.ts CHANGED
@@ -4,8 +4,9 @@
4
4
  // value site with the sucrase tokenizer, and reports the same diagnostics the
5
5
  // editor plugin and eslint rule produce, formatted as readable code frames.
6
6
 
7
- import { readdirSync, readFileSync } from 'node:fs'
8
- import { join, relative } from 'node:path'
7
+ import { existsSync, readdirSync, readFileSync } from 'node:fs'
8
+ import { dirname, join, relative, resolve } from 'node:path'
9
+ import { spawnSync } from 'node:child_process'
9
10
  // deep paths, spelled down to the file: sucrase publishes no `exports` map, and
10
11
  // a bare `sucrase/dist/parser` is a directory import that ESM cannot resolve
11
12
  import { parse } from 'sucrase/dist/parser/index.js'
@@ -31,6 +32,13 @@ const skippedDirectories = new Set([
31
32
  '.expo',
32
33
  '.tamagui',
33
34
  ])
35
+ const projectMarkers = [
36
+ 'tamagui.build.ts',
37
+ 'tamagui.build.tsx',
38
+ 'tamagui.build.js',
39
+ 'tamagui.build.mjs',
40
+ 'tamagui.build.cjs',
41
+ ]
34
42
 
35
43
  export interface CheckStyleFilesOptions {
36
44
  /** project root; file discovery and relative display paths anchor here */
@@ -39,6 +47,8 @@ export interface CheckStyleFilesOptions {
39
47
  configPath?: string
40
48
  /** explicit files to check instead of walking the root */
41
49
  files?: readonly string[]
50
+ /** validate clause payloads strictly against token and keyword vocabularies */
51
+ strict?: boolean
42
52
  }
43
53
 
44
54
  export interface CheckedFile {
@@ -52,6 +62,8 @@ export interface CheckStyleFilesResult {
52
62
  files: readonly CheckedFile[]
53
63
  checkedFileCount: number
54
64
  diagnosticCount: number
65
+ /** root-relative Tamagui projects omitted because they own another config */
66
+ skippedProjects: readonly string[]
55
67
  }
56
68
 
57
69
  export class MissingConfigArtifactError extends Error {
@@ -63,12 +75,24 @@ export class MissingConfigArtifactError extends Error {
63
75
  }
64
76
  }
65
77
 
66
- function walkSourceFiles(directory: string, results: string[]): void {
78
+ function walkSourceFiles(
79
+ directory: string,
80
+ root: string,
81
+ results: string[],
82
+ skippedProjects: string[]
83
+ ): void {
67
84
  for (const entry of readdirSync(directory, { withFileTypes: true })) {
68
85
  if (entry.isDirectory()) {
69
- if (!skippedDirectories.has(entry.name) && !entry.name.startsWith('.')) {
70
- walkSourceFiles(join(directory, entry.name), results)
86
+ if (skippedDirectories.has(entry.name) || entry.name.startsWith('.')) continue
87
+ const child = join(directory, entry.name)
88
+ if (
89
+ projectMarkers.some((marker) => existsSync(join(child, marker))) ||
90
+ existsSync(join(child, '.tamagui', 'tamagui.config.json'))
91
+ ) {
92
+ skippedProjects.push(relative(root, child))
93
+ continue
71
94
  }
95
+ walkSourceFiles(child, root, results, skippedProjects)
72
96
  continue
73
97
  }
74
98
  if (sourceExtensions.test(entry.name)) results.push(join(directory, entry.name))
@@ -82,29 +106,97 @@ export function createProjectExtractor(
82
106
  }
83
107
 
84
108
  export function checkStyleFiles(options: CheckStyleFilesOptions): CheckStyleFilesResult {
85
- const configPath =
86
- options.configPath ?? join(options.root, '.tamagui', 'tamagui.config.json')
109
+ const root = resolve(options.root)
110
+ const configPath = options.configPath ?? join(root, '.tamagui', 'tamagui.config.json')
87
111
  let contents: string
88
112
  try {
89
113
  contents = readFileSync(configPath, 'utf8')
90
114
  } catch {
91
115
  throw new MissingConfigArtifactError(configPath)
92
116
  }
93
- const tooling = createStyleTooling(JSON.parse(contents) as SerializedConfigFile)
117
+ const tooling = createStyleTooling(JSON.parse(contents) as SerializedConfigFile, {
118
+ strictPayloads: options.strict,
119
+ })
94
120
  if (!tooling) throw new MissingConfigArtifactError(configPath)
95
121
 
96
122
  const document = createDocumentStyleTooling(
97
123
  tooling,
98
- createProjectExtractor((name) => tooling.isStyleProp(name))
124
+ createProjectExtractor((name) => tooling.isStyleProp(name)),
125
+ { strictPayloads: options.strict }
99
126
  )
100
127
 
101
128
  let files: string[]
129
+ const skippedProjects: string[] = []
102
130
  if (options.files) {
103
131
  files = [...options.files]
104
132
  } else {
105
133
  files = []
106
- walkSourceFiles(options.root, files)
134
+ const insideGit = spawnSync('git', ['rev-parse', '--is-inside-work-tree'], {
135
+ cwd: root,
136
+ encoding: 'utf8',
137
+ })
138
+ if (insideGit.status === 0) {
139
+ const listed = spawnSync(
140
+ 'git',
141
+ [
142
+ 'ls-files',
143
+ '--cached',
144
+ '--others',
145
+ '--exclude-standard',
146
+ '-z',
147
+ '--',
148
+ '*.tsx',
149
+ '*.jsx',
150
+ ],
151
+ {
152
+ cwd: root,
153
+ encoding: 'utf8',
154
+ maxBuffer: 64 * 1024 * 1024,
155
+ }
156
+ )
157
+ if (listed.error) throw listed.error
158
+ if (listed.status !== 0) {
159
+ throw new Error(listed.stderr.trim() || 'git ls-files failed')
160
+ }
161
+ const skipped = new Set<string>()
162
+ const projectByDirectory = new Map<string, string | null>()
163
+ for (const relativeFile of listed.stdout.split('\0')) {
164
+ if (!relativeFile) continue
165
+ const file = join(root, relativeFile)
166
+ if (!existsSync(file)) continue
167
+ let directory = dirname(file)
168
+ let nestedProject: string | null = null
169
+ const visited: string[] = []
170
+ while (directory !== root) {
171
+ if (projectByDirectory.has(directory)) {
172
+ nestedProject = projectByDirectory.get(directory) ?? null
173
+ break
174
+ }
175
+ visited.push(directory)
176
+ if (
177
+ projectMarkers.some((marker) => existsSync(join(directory, marker))) ||
178
+ existsSync(join(directory, '.tamagui', 'tamagui.config.json'))
179
+ ) {
180
+ nestedProject = directory
181
+ break
182
+ }
183
+ directory = dirname(directory)
184
+ }
185
+ for (const visitedDirectory of visited) {
186
+ projectByDirectory.set(visitedDirectory, nestedProject)
187
+ }
188
+ if (nestedProject) {
189
+ skipped.add(relative(root, nestedProject))
190
+ } else {
191
+ files.push(file)
192
+ }
193
+ }
194
+ skippedProjects.push(...skipped)
195
+ } else {
196
+ walkSourceFiles(root, root, files, skippedProjects)
197
+ }
107
198
  files.sort()
199
+ skippedProjects.sort()
108
200
  }
109
201
 
110
202
  const checked: CheckedFile[] = []
@@ -118,7 +210,9 @@ export function checkStyleFiles(options: CheckStyleFilesOptions): CheckStyleFile
118
210
  }
119
211
  let diagnostics: readonly DocumentDiagnostic[]
120
212
  try {
121
- diagnostics = document.diagnostics(source)
213
+ diagnostics = document.diagnostics(source, undefined, {
214
+ strictPayloads: options.strict,
215
+ })
122
216
  } catch {
123
217
  // a file sucrase cannot parse is a syntax error the real compiler will
124
218
  // report; the style checker stays quiet about it
@@ -126,10 +220,15 @@ export function checkStyleFiles(options: CheckStyleFilesOptions): CheckStyleFile
126
220
  }
127
221
  if (diagnostics.length === 0) continue
128
222
  diagnosticCount += diagnostics.length
129
- checked.push({ file: relative(options.root, file), source, diagnostics })
223
+ checked.push({ file: relative(root, file), source, diagnostics })
130
224
  }
131
225
 
132
- return { files: checked, checkedFileCount: files.length, diagnosticCount }
226
+ return {
227
+ files: checked,
228
+ checkedFileCount: files.length,
229
+ diagnosticCount,
230
+ skippedProjects,
231
+ }
133
232
  }
134
233
 
135
234
  const ansi = {
@@ -195,6 +294,12 @@ export function formatCheckResults(
195
294
  }
196
295
  }
197
296
 
297
+ if (result.skippedProjects.length > 0) {
298
+ output.push(
299
+ paint.dim(`Skipped nested Tamagui projects: ${result.skippedProjects.join(', ')}`)
300
+ )
301
+ }
302
+
198
303
  const summary =
199
304
  result.diagnosticCount === 0
200
305
  ? `${paint.bold('✓')} ${result.checkedFileCount} files, no flat value problems`