@multiplatform.one/config 7.23.0 → 7.23.2
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/README.md +5 -2
- package/oxlint.mjs +13 -10
- package/package.json +2 -2
- package/src/oxlint.spec.ts +31 -0
- package/src/singletonDepAliases.spec.ts +10 -3
package/README.md
CHANGED
|
@@ -76,8 +76,8 @@ computed colours, from all but `no-raw-typography`. A project that wants its
|
|
|
76
76
|
own levels keeps the older one-line form,
|
|
77
77
|
`{ "jsPlugins": ["@multiplatform.one/config/oxlint"] }`, and writes its own
|
|
78
78
|
rules block (mpo's own `oxlintrc.json` turns `no-hex-literals` on for
|
|
79
|
-
`apps/**`, `packages
|
|
80
|
-
trees). Palette modules (`themes/base.ts`, `themes/accent.ts`,
|
|
79
|
+
`apps/**`, `packages/**`, `features/**`, `public/frappe-ui/src` and
|
|
80
|
+
`public/backoffice/src`, and off for specs and `tests/` trees). Palette modules (`themes/base.ts`, `themes/accent.ts`,
|
|
81
81
|
`*palette*`) are exempt. Comments are not visited, so a JSDoc `#025` is not
|
|
82
82
|
a hit. A file no theme token reaches opts out with a `// hex-escape: <reason>`
|
|
83
83
|
line comment; `lint` fails that comment unless the spec table
|
|
@@ -87,6 +87,9 @@ whose comment is gone (MPO-326).
|
|
|
87
87
|
`no-chrome-props` and `no-raw-geometry` ship in the same plugin (MPO-17).
|
|
88
88
|
Enable them the same way when you start that burndown. Drawing-file
|
|
89
89
|
exceptions come from the spec table `## Drawing files`, not from the code.
|
|
90
|
+
`no-raw-geometry` leaves `0` alone (`00` L8 E1). mpo's own `oxlintrc.json`
|
|
91
|
+
runs it as an error over `public/frappe-ui/src` and `public/backoffice/src`
|
|
92
|
+
(MPO-15), off for specs.
|
|
90
93
|
|
|
91
94
|
`no-child-margin` (MPO-23, `00` L9a) fails a non-zero `margin*` prop, long
|
|
92
95
|
or shorthand, on any element nested inside another element's JSX: the parent
|
package/oxlint.mjs
CHANGED
|
@@ -23,9 +23,10 @@
|
|
|
23
23
|
* stay legal. Exceptions come from the spec DRAWING table's CHROME-ALLOW
|
|
24
24
|
* row, not from the code (MPO-17).
|
|
25
25
|
* mpo-conventions/no-raw-geometry — ban numeric height/width/padding/fontSize
|
|
26
|
-
* on those same imports.
|
|
27
|
-
* file listed as DRAWING. An
|
|
28
|
-
* lint.mjs failure, not a skip
|
|
26
|
+
* on those same imports. `0` is `00` L8 E1 and stays legal. Silent
|
|
27
|
+
* behind sizeRecipeEscape() and inside a file listed as DRAWING. An
|
|
28
|
+
* undeclared `// mpo-drawing` pragma is a lint.mjs failure, not a skip
|
|
29
|
+
* here.
|
|
29
30
|
* mpo-conventions/no-child-margin — `00` L9a: the parent owns the gap. Ban a
|
|
30
31
|
* non-zero margin* prop on any element nested inside another element's
|
|
31
32
|
* JSX. The outermost element of a tree is left alone (a file cannot see
|
|
@@ -182,7 +183,7 @@ export const MARGIN_PROPS = Object.freeze([
|
|
|
182
183
|
|
|
183
184
|
const CHROME_PROP_SET = new Set(CHROME_PROPS);
|
|
184
185
|
const MARGIN_PROP_SET = new Set(MARGIN_PROPS);
|
|
185
|
-
const
|
|
186
|
+
const ZERO_VALUE = /^(?:-?0(?:\.0+)?(?:px)?|\$0)$/;
|
|
186
187
|
const GEOMETRY_PROP_SET = new Set(GEOMETRY_PROPS);
|
|
187
188
|
|
|
188
189
|
const RULE_OPTIONS_SCHEMA = [
|
|
@@ -222,19 +223,20 @@ function resolveSlotMargins(context) {
|
|
|
222
223
|
|
|
223
224
|
/**
|
|
224
225
|
* `0` is `00` L8 E1, legal anywhere: a zero-reset is the absence of an outer
|
|
225
|
-
* margin, not one (Tamagui's own headings ship `margin: 0
|
|
226
|
+
* margin or a size, not one (Tamagui's own headings ship `margin: 0`, and
|
|
227
|
+
* `minWidth={0}` is what lets a flex child shrink below its content).
|
|
226
228
|
* @param {unknown} value
|
|
227
229
|
*/
|
|
228
|
-
function
|
|
230
|
+
function isZeroReset(value) {
|
|
229
231
|
const node = unwrapJsxValue(value);
|
|
230
232
|
if (!node || typeof node !== "object") return false;
|
|
231
233
|
const expr =
|
|
232
234
|
/** @type {{ type?: string, value?: unknown, operator?: string, argument?: unknown }} */ (node);
|
|
233
235
|
if (expr.type === "Literal") {
|
|
234
236
|
if (typeof expr.value === "number") return expr.value === 0;
|
|
235
|
-
if (typeof expr.value === "string") return
|
|
237
|
+
if (typeof expr.value === "string") return ZERO_VALUE.test(expr.value.trim());
|
|
236
238
|
}
|
|
237
|
-
if (expr.type === "UnaryExpression" && expr.operator === "-") return
|
|
239
|
+
if (expr.type === "UnaryExpression" && expr.operator === "-") return isZeroReset(expr.argument);
|
|
238
240
|
return false;
|
|
239
241
|
}
|
|
240
242
|
|
|
@@ -541,7 +543,7 @@ export const plugin = {
|
|
|
541
543
|
type: "problem",
|
|
542
544
|
docs: {
|
|
543
545
|
description:
|
|
544
|
-
"Ban numeric geometry on imported @multiplatform.one/* components unless wrapped in sizeRecipeEscape or the file is a declared drawing.",
|
|
546
|
+
"Ban non-zero numeric geometry on imported @multiplatform.one/* components unless wrapped in sizeRecipeEscape or the file is a declared drawing.",
|
|
545
547
|
},
|
|
546
548
|
schema: RULE_OPTIONS_SCHEMA,
|
|
547
549
|
messages: {
|
|
@@ -557,6 +559,7 @@ export const plugin = {
|
|
|
557
559
|
if (!GEOMETRY_PROP_SET.has(prop)) continue;
|
|
558
560
|
if (isSizeRecipeEscape(attr.value)) continue;
|
|
559
561
|
if (!isNumericLiteral(attr.value)) continue;
|
|
562
|
+
if (isZeroReset(attr.value)) continue;
|
|
560
563
|
context.report({
|
|
561
564
|
node: attr,
|
|
562
565
|
messageId: "geometry",
|
|
@@ -601,7 +604,7 @@ export const plugin = {
|
|
|
601
604
|
for (const attr of node.attributes ?? []) {
|
|
602
605
|
const prop = jsxAttrName(attr);
|
|
603
606
|
if (!MARGIN_PROP_SET.has(prop)) continue;
|
|
604
|
-
if (
|
|
607
|
+
if (isZeroReset(attr.value)) continue;
|
|
605
608
|
context.report({
|
|
606
609
|
node: attr,
|
|
607
610
|
messageId: "childMargin",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@multiplatform.one/config",
|
|
3
|
-
"version": "7.23.
|
|
3
|
+
"version": "7.23.2",
|
|
4
4
|
"description": "Shared build and test configuration presets for multiplatform.one",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"config",
|
|
@@ -99,7 +99,7 @@
|
|
|
99
99
|
"access": "public"
|
|
100
100
|
},
|
|
101
101
|
"dependencies": {
|
|
102
|
-
"@multiplatform.one/utils": "7.23.
|
|
102
|
+
"@multiplatform.one/utils": "7.23.2",
|
|
103
103
|
"@tamagui/vite-plugin": "2.7.6",
|
|
104
104
|
"@vitejs/plugin-react": "^6.0.1",
|
|
105
105
|
"dotenv": "^17.4.2",
|
package/src/oxlint.spec.ts
CHANGED
|
@@ -266,6 +266,37 @@ describe("mpo-conventions/no-raw-geometry (MPO-17)", () => {
|
|
|
266
266
|
expect(wrapped).toEqual([]);
|
|
267
267
|
});
|
|
268
268
|
|
|
269
|
+
it("never flags a zero-reset (00 L8 E1) but still flags any other number", () => {
|
|
270
|
+
const zero = driveHouseRule("no-raw-geometry", filename, [
|
|
271
|
+
importCard,
|
|
272
|
+
jsxOpen("Card", [
|
|
273
|
+
{ prop: "minWidth", value: numeric(0) },
|
|
274
|
+
{ prop: "padding", value: numeric(0) },
|
|
275
|
+
{
|
|
276
|
+
prop: "minHeight",
|
|
277
|
+
value: {
|
|
278
|
+
type: "JSXExpressionContainer",
|
|
279
|
+
expression: {
|
|
280
|
+
type: "UnaryExpression",
|
|
281
|
+
operator: "-",
|
|
282
|
+
argument: { type: "Literal", value: 0 },
|
|
283
|
+
},
|
|
284
|
+
},
|
|
285
|
+
},
|
|
286
|
+
]),
|
|
287
|
+
]);
|
|
288
|
+
expect(zero).toEqual([]);
|
|
289
|
+
|
|
290
|
+
const small = driveHouseRule("no-raw-geometry", filename, [
|
|
291
|
+
importCard,
|
|
292
|
+
jsxOpen("Card", [
|
|
293
|
+
{ prop: "padding", value: numeric(2) },
|
|
294
|
+
{ prop: "minWidth", value: numeric(0.5) },
|
|
295
|
+
]),
|
|
296
|
+
]);
|
|
297
|
+
expect(small.map((r) => r.data?.prop)).toEqual(["padding", "minWidth"]);
|
|
298
|
+
});
|
|
299
|
+
|
|
269
300
|
it("stays silent inside a registered drawing file", () => {
|
|
270
301
|
expect(
|
|
271
302
|
driveHouseRule(
|
|
@@ -235,9 +235,10 @@ function declares(dir: string, pkgName: string): boolean {
|
|
|
235
235
|
}
|
|
236
236
|
|
|
237
237
|
/**
|
|
238
|
-
* Whether an installed package references `useSyncExternalStore` in
|
|
238
|
+
* Whether an installed package references `useSyncExternalStore` in the code
|
|
239
239
|
* it ships. The cheap half of the membership rule, re-run against the tree so
|
|
240
|
-
* the next tanstack upgrade is checked rather than assumed.
|
|
240
|
+
* the next tanstack upgrade is checked rather than assumed. Comments do not
|
|
241
|
+
* count: @tanstack/db 0.8 names the hook in doc comments and calls it nowhere.
|
|
241
242
|
*/
|
|
242
243
|
function subscribesExternalStore(pkgName: string): boolean {
|
|
243
244
|
const dir = path.join(workspaceRoot, "node_modules", ...pkgName.split("/"));
|
|
@@ -253,7 +254,13 @@ function referencesHook(dir: string): boolean {
|
|
|
253
254
|
continue;
|
|
254
255
|
}
|
|
255
256
|
if (!/\.(?:js|cjs|mjs|ts|tsx)$/.test(entry.name)) continue;
|
|
256
|
-
if (fs.readFileSync(full, "utf8").includes("useSyncExternalStore"))
|
|
257
|
+
if (withoutComments(fs.readFileSync(full, "utf8")).includes("useSyncExternalStore")) {
|
|
258
|
+
return true;
|
|
259
|
+
}
|
|
257
260
|
}
|
|
258
261
|
return false;
|
|
259
262
|
}
|
|
263
|
+
|
|
264
|
+
function withoutComments(source: string): string {
|
|
265
|
+
return source.replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|[^:\\])\/\/.*$/gm, "$1");
|
|
266
|
+
}
|