@zgeoff/oxlint-config 0.2.8 → 0.2.9

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 CHANGED
@@ -122,17 +122,21 @@ the boundaries between them. A file belongs to every zone its path matches, so z
122
122
  Each entry has a `name`, which starts its report, an optional `reason`, which ends it, and optional
123
123
  `from` zones and `ignore` globs that limit which importing files it judges.
124
124
 
125
- - `edges` judge an import that resolves to a file: relative, or the package's own name resolved
126
- through its `exports` under `conditions` plus `default`. A target in a `to` zone fails unless it
127
- is also in an `allow` zone; with `allow` alone, a target in none of the `allow` zones fails, a
128
- target in no zone included. `allowTypeOnly` exempts type-only imports (`true`, or globs the target
129
- must match), and `allowWithin: "src/*"` exempts an import inside the importer's own `src/<name>/`.
125
+ - `edges` judge an import that resolves to a file: relative, with any bundler query such as `?url`
126
+ dropped, or the package's own name resolved through its `exports` under `conditions` plus
127
+ `default`. A target in a `to` zone fails unless it is also in an `allow` zone; with `allow` alone,
128
+ a target in none of the `allow` zones fails, a target in no zone included. `allowTypeOnly` exempts
129
+ type-only imports (`true`, or globs the target must match), and `allowWithin: "src/*"` exempts an
130
+ import inside the importer's own `src/<name>/`.
130
131
  - `modules` restrict a package or builtin (`node:` prefix optional, subpaths included) to the
131
- `allow` zones; `allowTypeOnly: true` exempts type-only imports.
132
+ `allow` zones; `allowTypeOnly: true` exempts type-only imports. A name that ends in `*` restricts
133
+ every specifier that starts with the text before it, such as `@scope/*`, and `node:*` restricts
134
+ every builtin.
132
135
  - `globals` restrict a call to a global, bare or through `globalThis`, `self` or `window`.
133
136
  - `unzoned` reports a file in a `from` zone that belongs to none of `zones`.
134
137
  - `unresolved` reports a relative or self-referencing import that resolves to no file, and a dynamic
135
- `import()` or `require()` whose specifier is computed.
138
+ `import()`, `require()` or `import.meta.require()` whose specifier is computed. Bun's
139
+ `import.meta.require` used as a value counts as computed too.
136
140
 
137
141
  ```jsonc
138
142
  {
package/import-zones.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { isBuiltin } from 'node:module';
1
2
  import { relative, sep } from 'node:path';
2
3
  import findRepoRoot from './find-repo-root.js';
3
4
  import importZonesSchema from './import-zones-schema.js';
@@ -114,12 +115,25 @@ function isRestrictedModule(entry, specifier) {
114
115
  const name = toModuleName(specifier);
115
116
 
116
117
  return entry.modules.some((module) => {
118
+ if (module.endsWith('*')) {
119
+ return isPrefixMatch(module.slice(0, -1), specifier);
120
+ }
121
+
117
122
  const restricted = toModuleName(module);
118
123
 
119
124
  return name === restricted || name.startsWith(`${restricted}/`);
120
125
  });
121
126
  }
122
127
 
128
+ // `node:*` names every builtin, which a specifier may write without the prefix
129
+ function isPrefixMatch(prefix, specifier) {
130
+ if (specifier.startsWith(prefix)) {
131
+ return true;
132
+ }
133
+
134
+ return prefix === 'node:' && isBuiltin(specifier);
135
+ }
136
+
123
137
  function toModuleName(specifier) {
124
138
  return specifier.replace(/^node:/u, '');
125
139
  }
@@ -235,6 +249,18 @@ function isTypeOnlyExport(node) {
235
249
  );
236
250
  }
237
251
 
252
+ function isImportMetaRequire(node) {
253
+ return (
254
+ node.type === 'MemberExpression' &&
255
+ !node.computed &&
256
+ node.object.type === 'MetaProperty' &&
257
+ node.object.meta.name === 'import' &&
258
+ node.object.property.name === 'meta' &&
259
+ node.property.type === 'Identifier' &&
260
+ node.property.name === 'require'
261
+ );
262
+ }
263
+
238
264
  const importZones = {
239
265
  meta: {
240
266
  type: 'problem',
@@ -284,10 +310,21 @@ const importZones = {
284
310
  CallExpression(node) {
285
311
  checkGlobalCall(file, node);
286
312
 
287
- if (node.callee.type === 'Identifier' && node.callee.name === 'require') {
313
+ if (
314
+ (node.callee.type === 'Identifier' && node.callee.name === 'require') ||
315
+ isImportMetaRequire(node.callee)
316
+ ) {
288
317
  checkImport(file, { node, source: node.arguments[0], typeOnly: false });
289
318
  }
290
319
  },
320
+
321
+ // Bun's import.meta.require used as a value, such as `const load =
322
+ // import.meta.require`, loads modules no specifier names
323
+ MemberExpression(node) {
324
+ if (isImportMetaRequire(node) && node.parent?.callee !== node) {
325
+ emitUnresolved(file, node, { messageId: 'computed', data: {} });
326
+ }
327
+ },
291
328
  };
292
329
  },
293
330
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zgeoff/oxlint-config",
3
- "version": "0.2.8",
3
+ "version": "0.2.9",
4
4
  "description": "Shareable oxlint config: strict categories, house-style rules, and a custom JS plugin",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -9,7 +9,7 @@ const sourceExtensions = { '.js': ['.ts', '.tsx'], '.mjs': ['.mts'], '.cjs': ['.
9
9
  // own name resolves through its `exports`, and `#` names through its `imports`.
10
10
  export default function resolveImportTarget(filename, specifier, settings) {
11
11
  if (isPathSpecifier(specifier)) {
12
- return toTarget(findImportedFile(filename, specifier, settings.extensions));
12
+ return toTarget(findImportedFile(filename, toFileSpecifier(specifier), settings.extensions));
13
13
  }
14
14
 
15
15
  const manifest = findPackageManifest(dirname(filename), settings.root);
@@ -40,6 +40,13 @@ function resolveManifestTarget(manifest, specifier, conditions) {
40
40
  return { kind: 'module', specifier };
41
41
  }
42
42
 
43
+ // a bundler query such as Vite's `./styles.css?url` names the file before it
44
+ function toFileSpecifier(specifier) {
45
+ const query = specifier.indexOf('?');
46
+
47
+ return query === -1 ? specifier : specifier.slice(0, query);
48
+ }
49
+
43
50
  function toTarget(path) {
44
51
  return path === null ? { kind: 'missing' } : { kind: 'file', path };
45
52
  }