@cmssy/eslint-plugin 12.7.0 → 12.8.0
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/dist/index.cjs +93 -5
- package/dist/index.d.cts +5 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +93 -6
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -5,7 +5,90 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
5
5
|
var fs = require('fs');
|
|
6
6
|
var path = require('path');
|
|
7
7
|
|
|
8
|
-
// src/
|
|
8
|
+
// src/edit-route-provider-parity.ts
|
|
9
|
+
var LAYOUT = /\/layout\.(tsx|jsx|js|ts)$/;
|
|
10
|
+
var EDIT_SEGMENT = "cmssy-edit";
|
|
11
|
+
var WRAPPERS = /* @__PURE__ */ new Set([
|
|
12
|
+
"LazyMotion",
|
|
13
|
+
"MotionConfig",
|
|
14
|
+
"AnimatePresence",
|
|
15
|
+
"MotionProvider"
|
|
16
|
+
]);
|
|
17
|
+
function normalize(path$1) {
|
|
18
|
+
return path$1.split(path.sep).join("/");
|
|
19
|
+
}
|
|
20
|
+
function isWrapper(name) {
|
|
21
|
+
return name.endsWith("Provider") || WRAPPERS.has(name);
|
|
22
|
+
}
|
|
23
|
+
function elementName(node) {
|
|
24
|
+
const opening = node.openingElement;
|
|
25
|
+
const name = opening?.name ?? {};
|
|
26
|
+
return name.type === "JSXIdentifier" ? name.name ?? null : null;
|
|
27
|
+
}
|
|
28
|
+
function editCounterpart(filename) {
|
|
29
|
+
const file = normalize(filename);
|
|
30
|
+
if (!LAYOUT.test(file)) return null;
|
|
31
|
+
const app = file.lastIndexOf("/app/");
|
|
32
|
+
if (app === -1) return null;
|
|
33
|
+
const rest = file.slice(app + "/app/".length);
|
|
34
|
+
if (!rest.includes("/")) return null;
|
|
35
|
+
if (rest.startsWith(`${EDIT_SEGMENT}/`)) return null;
|
|
36
|
+
return `${file.slice(0, app)}/app/${EDIT_SEGMENT}/${rest}`;
|
|
37
|
+
}
|
|
38
|
+
function rendersElement(code, name) {
|
|
39
|
+
const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
40
|
+
return new RegExp(`<${escaped}[\\s/>]`).test(code);
|
|
41
|
+
}
|
|
42
|
+
function isChildrenSlot(node) {
|
|
43
|
+
const container = node;
|
|
44
|
+
return container.type === "JSXExpressionContainer" && container.expression?.type === "Identifier" && container.expression.name === "children";
|
|
45
|
+
}
|
|
46
|
+
function wrapsChildren(node) {
|
|
47
|
+
const children = node.children ?? [];
|
|
48
|
+
return children.some(
|
|
49
|
+
(child) => isChildrenSlot(child) || wrapsChildren(child)
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
var editRouteProviderParity = {
|
|
53
|
+
meta: {
|
|
54
|
+
type: "problem",
|
|
55
|
+
docs: {
|
|
56
|
+
description: "Require a provider that wraps the public route's children to wrap the cmssy edit route's children too"
|
|
57
|
+
},
|
|
58
|
+
schema: [],
|
|
59
|
+
messages: {
|
|
60
|
+
missingProvider: "<{{name}}> wraps the blocks on the public route but not on {{editRoute}}, so the editor renders them without whatever it provides. A missing animation provider is the loudest case - the reveals never attach and the preview stays blank - but any context the blocks read is gone the same way.\nMove it into cmssy/site-providers.tsx and render that from both roots, or repeat <{{name}}> in the edit layout."
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
create(context) {
|
|
64
|
+
const filename = context.filename ?? context.getFilename();
|
|
65
|
+
const counterpart = editCounterpart(filename);
|
|
66
|
+
if (!counterpart) return {};
|
|
67
|
+
let editCode;
|
|
68
|
+
try {
|
|
69
|
+
editCode = fs.readFileSync(counterpart, "utf8");
|
|
70
|
+
} catch {
|
|
71
|
+
return {};
|
|
72
|
+
}
|
|
73
|
+
const editRoute = counterpart.slice(counterpart.lastIndexOf("/app/") + 1);
|
|
74
|
+
const reported = /* @__PURE__ */ new Set();
|
|
75
|
+
return {
|
|
76
|
+
JSXElement(node) {
|
|
77
|
+
const name = elementName(node);
|
|
78
|
+
if (!name || reported.has(name)) return;
|
|
79
|
+
if (!isWrapper(name)) return;
|
|
80
|
+
if (!wrapsChildren(node)) return;
|
|
81
|
+
if (rendersElement(editCode, name)) return;
|
|
82
|
+
reported.add(name);
|
|
83
|
+
context.report({
|
|
84
|
+
node,
|
|
85
|
+
messageId: "missingProvider",
|
|
86
|
+
data: { name, editRoute }
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
};
|
|
9
92
|
var SERVER_MODULES = [/^@cmssy\/next\/server$/];
|
|
10
93
|
var SERVER_SYMBOLS = /* @__PURE__ */ new Set(["defineCmssyConfig"]);
|
|
11
94
|
var EXTENSIONS = ["", ".ts", ".tsx", ".js", ".jsx", ".mjs"];
|
|
@@ -186,13 +269,13 @@ function chainToServerConfig(file, cache2, seen = /* @__PURE__ */ new Set()) {
|
|
|
186
269
|
cache2.set(file, null);
|
|
187
270
|
return null;
|
|
188
271
|
}
|
|
189
|
-
function
|
|
272
|
+
function normalize2(path$1) {
|
|
190
273
|
return path$1.split(path.sep).join("/");
|
|
191
274
|
}
|
|
192
275
|
function isClientEntry(filename, entries) {
|
|
193
|
-
const file =
|
|
276
|
+
const file = normalize2(filename);
|
|
194
277
|
return entries.some((entry) => {
|
|
195
|
-
const wanted =
|
|
278
|
+
const wanted = normalize2(entry);
|
|
196
279
|
if (path.isAbsolute(wanted)) return file === wanted;
|
|
197
280
|
return file === wanted || file.endsWith(`/${wanted}`);
|
|
198
281
|
});
|
|
@@ -263,6 +346,7 @@ var noServerConfigInClient = {
|
|
|
263
346
|
|
|
264
347
|
// src/index.ts
|
|
265
348
|
var rules = {
|
|
349
|
+
"edit-route-provider-parity": editRouteProviderParity,
|
|
266
350
|
"no-server-config-in-client": noServerConfigInClient
|
|
267
351
|
};
|
|
268
352
|
var plugin = {
|
|
@@ -273,11 +357,15 @@ var plugin = {
|
|
|
273
357
|
plugin.configs.recommended = [
|
|
274
358
|
{
|
|
275
359
|
plugins: { cmssy: plugin },
|
|
276
|
-
rules: {
|
|
360
|
+
rules: {
|
|
361
|
+
"cmssy/edit-route-provider-parity": "error",
|
|
362
|
+
"cmssy/no-server-config-in-client": "error"
|
|
363
|
+
}
|
|
277
364
|
}
|
|
278
365
|
];
|
|
279
366
|
var index_default = plugin;
|
|
280
367
|
|
|
281
368
|
exports.default = index_default;
|
|
369
|
+
exports.editRouteProviderParity = editRouteProviderParity;
|
|
282
370
|
exports.noServerConfigInClient = noServerConfigInClient;
|
|
283
371
|
exports.rules = rules;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import * as eslint from 'eslint';
|
|
2
2
|
import { Rule } from 'eslint';
|
|
3
3
|
|
|
4
|
+
declare const editRouteProviderParity: Rule.RuleModule;
|
|
5
|
+
|
|
4
6
|
declare const noServerConfigInClient: Rule.RuleModule;
|
|
5
7
|
|
|
6
8
|
declare const rules: {
|
|
9
|
+
"edit-route-provider-parity": eslint.Rule.RuleModule;
|
|
7
10
|
"no-server-config-in-client": eslint.Rule.RuleModule;
|
|
8
11
|
};
|
|
9
12
|
declare const plugin: {
|
|
@@ -11,9 +14,10 @@ declare const plugin: {
|
|
|
11
14
|
name: string;
|
|
12
15
|
};
|
|
13
16
|
rules: {
|
|
17
|
+
"edit-route-provider-parity": eslint.Rule.RuleModule;
|
|
14
18
|
"no-server-config-in-client": eslint.Rule.RuleModule;
|
|
15
19
|
};
|
|
16
20
|
configs: Record<string, unknown>;
|
|
17
21
|
};
|
|
18
22
|
|
|
19
|
-
export { plugin as default, noServerConfigInClient, rules };
|
|
23
|
+
export { plugin as default, editRouteProviderParity, noServerConfigInClient, rules };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import * as eslint from 'eslint';
|
|
2
2
|
import { Rule } from 'eslint';
|
|
3
3
|
|
|
4
|
+
declare const editRouteProviderParity: Rule.RuleModule;
|
|
5
|
+
|
|
4
6
|
declare const noServerConfigInClient: Rule.RuleModule;
|
|
5
7
|
|
|
6
8
|
declare const rules: {
|
|
9
|
+
"edit-route-provider-parity": eslint.Rule.RuleModule;
|
|
7
10
|
"no-server-config-in-client": eslint.Rule.RuleModule;
|
|
8
11
|
};
|
|
9
12
|
declare const plugin: {
|
|
@@ -11,9 +14,10 @@ declare const plugin: {
|
|
|
11
14
|
name: string;
|
|
12
15
|
};
|
|
13
16
|
rules: {
|
|
17
|
+
"edit-route-provider-parity": eslint.Rule.RuleModule;
|
|
14
18
|
"no-server-config-in-client": eslint.Rule.RuleModule;
|
|
15
19
|
};
|
|
16
20
|
configs: Record<string, unknown>;
|
|
17
21
|
};
|
|
18
22
|
|
|
19
|
-
export { plugin as default, noServerConfigInClient, rules };
|
|
23
|
+
export { plugin as default, editRouteProviderParity, noServerConfigInClient, rules };
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,90 @@
|
|
|
1
1
|
import { readFileSync, existsSync } from 'fs';
|
|
2
2
|
import { isAbsolute, resolve, dirname, sep } from 'path';
|
|
3
3
|
|
|
4
|
-
// src/
|
|
4
|
+
// src/edit-route-provider-parity.ts
|
|
5
|
+
var LAYOUT = /\/layout\.(tsx|jsx|js|ts)$/;
|
|
6
|
+
var EDIT_SEGMENT = "cmssy-edit";
|
|
7
|
+
var WRAPPERS = /* @__PURE__ */ new Set([
|
|
8
|
+
"LazyMotion",
|
|
9
|
+
"MotionConfig",
|
|
10
|
+
"AnimatePresence",
|
|
11
|
+
"MotionProvider"
|
|
12
|
+
]);
|
|
13
|
+
function normalize(path) {
|
|
14
|
+
return path.split(sep).join("/");
|
|
15
|
+
}
|
|
16
|
+
function isWrapper(name) {
|
|
17
|
+
return name.endsWith("Provider") || WRAPPERS.has(name);
|
|
18
|
+
}
|
|
19
|
+
function elementName(node) {
|
|
20
|
+
const opening = node.openingElement;
|
|
21
|
+
const name = opening?.name ?? {};
|
|
22
|
+
return name.type === "JSXIdentifier" ? name.name ?? null : null;
|
|
23
|
+
}
|
|
24
|
+
function editCounterpart(filename) {
|
|
25
|
+
const file = normalize(filename);
|
|
26
|
+
if (!LAYOUT.test(file)) return null;
|
|
27
|
+
const app = file.lastIndexOf("/app/");
|
|
28
|
+
if (app === -1) return null;
|
|
29
|
+
const rest = file.slice(app + "/app/".length);
|
|
30
|
+
if (!rest.includes("/")) return null;
|
|
31
|
+
if (rest.startsWith(`${EDIT_SEGMENT}/`)) return null;
|
|
32
|
+
return `${file.slice(0, app)}/app/${EDIT_SEGMENT}/${rest}`;
|
|
33
|
+
}
|
|
34
|
+
function rendersElement(code, name) {
|
|
35
|
+
const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
36
|
+
return new RegExp(`<${escaped}[\\s/>]`).test(code);
|
|
37
|
+
}
|
|
38
|
+
function isChildrenSlot(node) {
|
|
39
|
+
const container = node;
|
|
40
|
+
return container.type === "JSXExpressionContainer" && container.expression?.type === "Identifier" && container.expression.name === "children";
|
|
41
|
+
}
|
|
42
|
+
function wrapsChildren(node) {
|
|
43
|
+
const children = node.children ?? [];
|
|
44
|
+
return children.some(
|
|
45
|
+
(child) => isChildrenSlot(child) || wrapsChildren(child)
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
var editRouteProviderParity = {
|
|
49
|
+
meta: {
|
|
50
|
+
type: "problem",
|
|
51
|
+
docs: {
|
|
52
|
+
description: "Require a provider that wraps the public route's children to wrap the cmssy edit route's children too"
|
|
53
|
+
},
|
|
54
|
+
schema: [],
|
|
55
|
+
messages: {
|
|
56
|
+
missingProvider: "<{{name}}> wraps the blocks on the public route but not on {{editRoute}}, so the editor renders them without whatever it provides. A missing animation provider is the loudest case - the reveals never attach and the preview stays blank - but any context the blocks read is gone the same way.\nMove it into cmssy/site-providers.tsx and render that from both roots, or repeat <{{name}}> in the edit layout."
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
create(context) {
|
|
60
|
+
const filename = context.filename ?? context.getFilename();
|
|
61
|
+
const counterpart = editCounterpart(filename);
|
|
62
|
+
if (!counterpart) return {};
|
|
63
|
+
let editCode;
|
|
64
|
+
try {
|
|
65
|
+
editCode = readFileSync(counterpart, "utf8");
|
|
66
|
+
} catch {
|
|
67
|
+
return {};
|
|
68
|
+
}
|
|
69
|
+
const editRoute = counterpart.slice(counterpart.lastIndexOf("/app/") + 1);
|
|
70
|
+
const reported = /* @__PURE__ */ new Set();
|
|
71
|
+
return {
|
|
72
|
+
JSXElement(node) {
|
|
73
|
+
const name = elementName(node);
|
|
74
|
+
if (!name || reported.has(name)) return;
|
|
75
|
+
if (!isWrapper(name)) return;
|
|
76
|
+
if (!wrapsChildren(node)) return;
|
|
77
|
+
if (rendersElement(editCode, name)) return;
|
|
78
|
+
reported.add(name);
|
|
79
|
+
context.report({
|
|
80
|
+
node,
|
|
81
|
+
messageId: "missingProvider",
|
|
82
|
+
data: { name, editRoute }
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
};
|
|
5
88
|
var SERVER_MODULES = [/^@cmssy\/next\/server$/];
|
|
6
89
|
var SERVER_SYMBOLS = /* @__PURE__ */ new Set(["defineCmssyConfig"]);
|
|
7
90
|
var EXTENSIONS = ["", ".ts", ".tsx", ".js", ".jsx", ".mjs"];
|
|
@@ -182,13 +265,13 @@ function chainToServerConfig(file, cache2, seen = /* @__PURE__ */ new Set()) {
|
|
|
182
265
|
cache2.set(file, null);
|
|
183
266
|
return null;
|
|
184
267
|
}
|
|
185
|
-
function
|
|
268
|
+
function normalize2(path) {
|
|
186
269
|
return path.split(sep).join("/");
|
|
187
270
|
}
|
|
188
271
|
function isClientEntry(filename, entries) {
|
|
189
|
-
const file =
|
|
272
|
+
const file = normalize2(filename);
|
|
190
273
|
return entries.some((entry) => {
|
|
191
|
-
const wanted =
|
|
274
|
+
const wanted = normalize2(entry);
|
|
192
275
|
if (isAbsolute(wanted)) return file === wanted;
|
|
193
276
|
return file === wanted || file.endsWith(`/${wanted}`);
|
|
194
277
|
});
|
|
@@ -259,6 +342,7 @@ var noServerConfigInClient = {
|
|
|
259
342
|
|
|
260
343
|
// src/index.ts
|
|
261
344
|
var rules = {
|
|
345
|
+
"edit-route-provider-parity": editRouteProviderParity,
|
|
262
346
|
"no-server-config-in-client": noServerConfigInClient
|
|
263
347
|
};
|
|
264
348
|
var plugin = {
|
|
@@ -269,9 +353,12 @@ var plugin = {
|
|
|
269
353
|
plugin.configs.recommended = [
|
|
270
354
|
{
|
|
271
355
|
plugins: { cmssy: plugin },
|
|
272
|
-
rules: {
|
|
356
|
+
rules: {
|
|
357
|
+
"cmssy/edit-route-provider-parity": "error",
|
|
358
|
+
"cmssy/no-server-config-in-client": "error"
|
|
359
|
+
}
|
|
273
360
|
}
|
|
274
361
|
];
|
|
275
362
|
var index_default = plugin;
|
|
276
363
|
|
|
277
|
-
export { index_default as default, noServerConfigInClient, rules };
|
|
364
|
+
export { index_default as default, editRouteProviderParity, noServerConfigInClient, rules };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cmssy/eslint-plugin",
|
|
3
|
-
"version": "12.
|
|
4
|
-
"description": "Catches the cmssy wiring mistakes that a build cannot: server config dragged into a client bundle.",
|
|
3
|
+
"version": "12.8.0",
|
|
4
|
+
"description": "Catches the cmssy wiring mistakes that a build cannot: server config dragged into a client bundle, a provider the editor route never gets.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cmssy",
|
|
7
7
|
"eslint",
|