@cmssy/eslint-plugin 12.13.0 → 12.14.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 +32 -3
- package/dist/index.js +32 -3
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -96,6 +96,11 @@ var editRouteProviderParity = {
|
|
|
96
96
|
};
|
|
97
97
|
var SERVER_MODULES = [/^@cmssy\/next\/server$/];
|
|
98
98
|
var SERVER_SYMBOLS = /* @__PURE__ */ new Set(["defineCmssyConfig"]);
|
|
99
|
+
var SERVER_ENV_NAME = "CMSSY_[A-Z0-9_]+";
|
|
100
|
+
var SERVER_ENV = new RegExp(`^${SERVER_ENV_NAME}$`);
|
|
101
|
+
var SERVER_ENV_READ = new RegExp(
|
|
102
|
+
`\\bprocess\\s*\\.\\s*env\\s*(?:\\.\\s*${SERVER_ENV_NAME}|\\[\\s*["'\`]${SERVER_ENV_NAME}["'\`]\\s*\\])`
|
|
103
|
+
);
|
|
99
104
|
var EXTENSIONS = ["", ".ts", ".tsx", ".js", ".jsx", ".mjs"];
|
|
100
105
|
var aliasCache = /* @__PURE__ */ new Map();
|
|
101
106
|
function stripJsonComments(text) {
|
|
@@ -223,7 +228,25 @@ function valueImports(code) {
|
|
|
223
228
|
...withoutTypes.matchAll(/(?:from|import)\s*["']([^"']+)["']/g)
|
|
224
229
|
].map(([, specifier]) => specifier ?? "");
|
|
225
230
|
}
|
|
231
|
+
function readsServerEnv(code) {
|
|
232
|
+
return SERVER_ENV_READ.test(code);
|
|
233
|
+
}
|
|
234
|
+
function serverEnvName(node) {
|
|
235
|
+
const member = node;
|
|
236
|
+
const env = member.object;
|
|
237
|
+
if (env?.type !== "MemberExpression" || env.computed) return null;
|
|
238
|
+
if (env.object?.type !== "Identifier" || env.object.name !== "process") {
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
241
|
+
if (env.property?.type !== "Identifier" || env.property.name !== "env") {
|
|
242
|
+
return null;
|
|
243
|
+
}
|
|
244
|
+
const property = member.property;
|
|
245
|
+
const name = member.computed ? typeof property?.value === "string" ? property.value : null : property?.type === "Identifier" ? property.name ?? null : null;
|
|
246
|
+
return name && SERVER_ENV.test(name) ? name : null;
|
|
247
|
+
}
|
|
226
248
|
function importsServerConfig(code) {
|
|
249
|
+
if (readsServerEnv(code)) return true;
|
|
227
250
|
if (SERVER_MODULES.some(
|
|
228
251
|
(pattern) => valueImports(code).some((s) => pattern.test(s))
|
|
229
252
|
)) {
|
|
@@ -290,7 +313,7 @@ var noServerConfigInClient = {
|
|
|
290
313
|
meta: {
|
|
291
314
|
type: "problem",
|
|
292
315
|
docs: {
|
|
293
|
-
description: "Disallow a client component from importing values that read the cmssy server config"
|
|
316
|
+
description: "Disallow a client component from importing values that read the cmssy server config, or reading a CMSSY_* variable itself"
|
|
294
317
|
},
|
|
295
318
|
schema: [
|
|
296
319
|
{
|
|
@@ -305,8 +328,9 @@ var noServerConfigInClient = {
|
|
|
305
328
|
}
|
|
306
329
|
],
|
|
307
330
|
messages: {
|
|
308
|
-
reachesConfig: 'This client component pulls the cmssy config into the browser bundle, where server env does not exist - the page
|
|
309
|
-
reachesConfigFromEntry: "This module is loaded in the browser, so it pulls the cmssy config there, where server env does not exist.\n {{chain}}\nImport the type instead (types are erased), or load the value with a dynamic import inside the function that needs it."
|
|
331
|
+
reachesConfig: 'This client component pulls the cmssy config into the browser bundle, where server env does not exist - the page fails at runtime with "missing required configuration", or reads whatever the config falls back to, which is usually "".\n {{chain}}\nImport the type instead (types are erased), or move the value into a module that does not touch the config.',
|
|
332
|
+
reachesConfigFromEntry: "This module is loaded in the browser, so it pulls the cmssy config there, where server env does not exist.\n {{chain}}\nImport the type instead (types are erased), or load the value with a dynamic import inside the function that needs it.",
|
|
333
|
+
readsServerEnv: "{{name}} is read here and this module runs in the browser, where it is undefined - the bundler only inlines NEXT_PUBLIC_* names, and a draft secret must never be one.\nRead it in a server component and pass the value down as a prop."
|
|
310
334
|
}
|
|
311
335
|
},
|
|
312
336
|
create(context) {
|
|
@@ -318,6 +342,11 @@ var noServerConfigInClient = {
|
|
|
318
342
|
if (!client && !entry) return {};
|
|
319
343
|
const messageId = client ? "reachesConfig" : "reachesConfigFromEntry";
|
|
320
344
|
return {
|
|
345
|
+
MemberExpression(node) {
|
|
346
|
+
const name = serverEnvName(node);
|
|
347
|
+
if (!name) return;
|
|
348
|
+
context.report({ node, messageId: "readsServerEnv", data: { name } });
|
|
349
|
+
},
|
|
321
350
|
ImportDeclaration(node) {
|
|
322
351
|
const typed = node;
|
|
323
352
|
const onlyTypes = typed.importKind === "type" || node.specifiers.length > 0 && node.specifiers.every(
|
package/dist/index.js
CHANGED
|
@@ -88,6 +88,11 @@ var editRouteProviderParity = {
|
|
|
88
88
|
};
|
|
89
89
|
var SERVER_MODULES = [/^@cmssy\/next\/server$/];
|
|
90
90
|
var SERVER_SYMBOLS = /* @__PURE__ */ new Set(["defineCmssyConfig"]);
|
|
91
|
+
var SERVER_ENV_NAME = "CMSSY_[A-Z0-9_]+";
|
|
92
|
+
var SERVER_ENV = new RegExp(`^${SERVER_ENV_NAME}$`);
|
|
93
|
+
var SERVER_ENV_READ = new RegExp(
|
|
94
|
+
`\\bprocess\\s*\\.\\s*env\\s*(?:\\.\\s*${SERVER_ENV_NAME}|\\[\\s*["'\`]${SERVER_ENV_NAME}["'\`]\\s*\\])`
|
|
95
|
+
);
|
|
91
96
|
var EXTENSIONS = ["", ".ts", ".tsx", ".js", ".jsx", ".mjs"];
|
|
92
97
|
var aliasCache = /* @__PURE__ */ new Map();
|
|
93
98
|
function stripJsonComments(text) {
|
|
@@ -215,7 +220,25 @@ function valueImports(code) {
|
|
|
215
220
|
...withoutTypes.matchAll(/(?:from|import)\s*["']([^"']+)["']/g)
|
|
216
221
|
].map(([, specifier]) => specifier ?? "");
|
|
217
222
|
}
|
|
223
|
+
function readsServerEnv(code) {
|
|
224
|
+
return SERVER_ENV_READ.test(code);
|
|
225
|
+
}
|
|
226
|
+
function serverEnvName(node) {
|
|
227
|
+
const member = node;
|
|
228
|
+
const env = member.object;
|
|
229
|
+
if (env?.type !== "MemberExpression" || env.computed) return null;
|
|
230
|
+
if (env.object?.type !== "Identifier" || env.object.name !== "process") {
|
|
231
|
+
return null;
|
|
232
|
+
}
|
|
233
|
+
if (env.property?.type !== "Identifier" || env.property.name !== "env") {
|
|
234
|
+
return null;
|
|
235
|
+
}
|
|
236
|
+
const property = member.property;
|
|
237
|
+
const name = member.computed ? typeof property?.value === "string" ? property.value : null : property?.type === "Identifier" ? property.name ?? null : null;
|
|
238
|
+
return name && SERVER_ENV.test(name) ? name : null;
|
|
239
|
+
}
|
|
218
240
|
function importsServerConfig(code) {
|
|
241
|
+
if (readsServerEnv(code)) return true;
|
|
219
242
|
if (SERVER_MODULES.some(
|
|
220
243
|
(pattern) => valueImports(code).some((s) => pattern.test(s))
|
|
221
244
|
)) {
|
|
@@ -282,7 +305,7 @@ var noServerConfigInClient = {
|
|
|
282
305
|
meta: {
|
|
283
306
|
type: "problem",
|
|
284
307
|
docs: {
|
|
285
|
-
description: "Disallow a client component from importing values that read the cmssy server config"
|
|
308
|
+
description: "Disallow a client component from importing values that read the cmssy server config, or reading a CMSSY_* variable itself"
|
|
286
309
|
},
|
|
287
310
|
schema: [
|
|
288
311
|
{
|
|
@@ -297,8 +320,9 @@ var noServerConfigInClient = {
|
|
|
297
320
|
}
|
|
298
321
|
],
|
|
299
322
|
messages: {
|
|
300
|
-
reachesConfig: 'This client component pulls the cmssy config into the browser bundle, where server env does not exist - the page
|
|
301
|
-
reachesConfigFromEntry: "This module is loaded in the browser, so it pulls the cmssy config there, where server env does not exist.\n {{chain}}\nImport the type instead (types are erased), or load the value with a dynamic import inside the function that needs it."
|
|
323
|
+
reachesConfig: 'This client component pulls the cmssy config into the browser bundle, where server env does not exist - the page fails at runtime with "missing required configuration", or reads whatever the config falls back to, which is usually "".\n {{chain}}\nImport the type instead (types are erased), or move the value into a module that does not touch the config.',
|
|
324
|
+
reachesConfigFromEntry: "This module is loaded in the browser, so it pulls the cmssy config there, where server env does not exist.\n {{chain}}\nImport the type instead (types are erased), or load the value with a dynamic import inside the function that needs it.",
|
|
325
|
+
readsServerEnv: "{{name}} is read here and this module runs in the browser, where it is undefined - the bundler only inlines NEXT_PUBLIC_* names, and a draft secret must never be one.\nRead it in a server component and pass the value down as a prop."
|
|
302
326
|
}
|
|
303
327
|
},
|
|
304
328
|
create(context) {
|
|
@@ -310,6 +334,11 @@ var noServerConfigInClient = {
|
|
|
310
334
|
if (!client && !entry) return {};
|
|
311
335
|
const messageId = client ? "reachesConfig" : "reachesConfigFromEntry";
|
|
312
336
|
return {
|
|
337
|
+
MemberExpression(node) {
|
|
338
|
+
const name = serverEnvName(node);
|
|
339
|
+
if (!name) return;
|
|
340
|
+
context.report({ node, messageId: "readsServerEnv", data: { name } });
|
|
341
|
+
},
|
|
313
342
|
ImportDeclaration(node) {
|
|
314
343
|
const typed = node;
|
|
315
344
|
const onlyTypes = typed.importKind === "type" || node.specifiers.length > 0 && node.specifiers.every(
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cmssy/eslint-plugin",
|
|
3
|
-
"version": "12.
|
|
3
|
+
"version": "12.14.0",
|
|
4
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",
|