Skip to content

feat(eslint-plugin-sdk): add lint rules for Node-only globals and built-in imports - #2313

Merged
dqn merged 7 commits into
mainfrom
feat/eslint-plugin-runtime-rules-g2
Sep 10, 2026
Merged

dqn merged 7 commits into
mainfrom
feat/eslint-plugin-runtime-rules-g2

Conversation

@dqn

@dqn dqn commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Summary

Two new lint rules report, in files that define a resolver, executor, workflow job, or HTTP adapter, the Node APIs the Tailor Platform runtime does not provide — previously rejected only after bundling.

Added rules (enabled at warn in scaffolded projects)

  • no-node-only-globalsprocess, Buffer, __dirname, require, and the other Node-only globals the runtime never defines
  • no-node-builtin-imports — static imports, re-exports, and static import() of node:fs, path, crypto, and other built-in modules

Example

import { readFile } from "node:fs/promises"; // no-node-builtin-imports

export default createResolver({
  name: "region",
  body: () => process.env.REGION, // no-node-only-globals
});

Both print the same suggested alternative the build does (Use \defineConfig({ env })`…, Use the Web Crypto API…`). The message tables move to the internal shared package so the CLI and the plugin cannot drift.

Notes

  • Only files defining a platform function are checked, so tailor.config.ts, scripts, and tests are unaffected. A Node API used in a helper module those files import is not reported; the build still checks the bundled output.
  • Locally bound names, property keys, type positions, typeof x guards, and globalThis.process are not reported, matching the CLI's free-variable scan.
  • In a file that only defines HTTP adapters the messages omit the suggestions, which are written for body functions.
  • Stacked on feat(eslint-plugin-sdk): add lint rules for workflow static constraints #2312; review that first.

dqn added 6 commits September 7, 2026 06:18
…in function files

Add two rules that report, in files defining a resolver, executor, workflow
job, or HTTP adapter, what the build otherwise rejects only after bundling:

- no-node-only-globals: references to the Node-only ambient globals the
  runtime never defines (process, Buffer, __dirname, require, ...). Locally
  bound names, property keys, type positions, and `typeof x` guards are
  ignored, mirroring the CLI's free-variable scan.
- no-node-builtin-imports: static imports, re-exports, and static `import()`
  of Node built-in modules. Type-only imports are ignored; `require()` is
  left to the globals rule.

Move the shared message tables and the Node-only global list into
`@tailor-platform/shared/node-builtins` so the SDK and the plugin print the
same suggestions; the SDK pins its `globals`-derived set to that list.
In files that only define HTTP adapters the messages omit the suggestions,
which are written for `body` functions.

Enable both rules at warn in every create-sdk template.
…bals

`typeof x !== "undefined" && typeof x.y !== "undefined" && x.y.z` and the
`||` / ternary counterparts guard every operand that follows, so walk the
logical chain instead of only its first operand.
…orts

- A `type` alias or `interface` sharing a Node-only global name does not
  bind a value, so the global reference is still reported.
- `export { type X } from "node:fs"` is erased at runtime and is no longer
  reported as a built-in import.
… ambient declarations

Sharing `isValueReference` had added `ExportSpecifier` to its exempt list,
which silenced `no-unconditional-permit` on an unsafe constant re-exported
with `export { permission }`. Treat the specifier's local side as a value
reference again and exempt only declaration binding names.

A `declare const process` erases at compile time, so it binds no runtime
value; `no-node-only-globals` now reports the global it appears to shadow.
… reference

`declare global {}` names the scope being augmented, so its `global`
identifier is not a reference to the Node global the runtime lacks.
@changeset-bot

changeset-bot Bot commented Sep 9, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 53195cc

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
@tailor-platform/eslint-plugin-sdk Minor
@tailor-platform/create-sdk Minor
@tailor-platform/sdk Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new

pkg-pr-new Bot commented Sep 9, 2026

Copy link
Copy Markdown

Open in StackBlitz

@tailor-platform/create-sdk

pnpm add https://pkg.pr.new/tailor-platform/sdk/@tailor-platform/create-sdk@53195cc

@tailor-platform/eslint-plugin-sdk

pnpm add https://pkg.pr.new/tailor-platform/sdk/@tailor-platform/eslint-plugin-sdk@53195cc

@tailor-platform/sdk

pnpm add https://pkg.pr.new/tailor-platform/sdk/@tailor-platform/sdk@53195cc

@tailor-platform/sdk-plugin-seed

pnpm add https://pkg.pr.new/tailor-platform/sdk/@tailor-platform/sdk-plugin-seed@53195cc

@tailor-platform/sdk-plugin-setup

pnpm add https://pkg.pr.new/tailor-platform/sdk/@tailor-platform/sdk-plugin-setup@53195cc

@tailor-platform/sdk-plugin-tailordb-erd

pnpm add https://pkg.pr.new/tailor-platform/sdk/@tailor-platform/sdk-plugin-tailordb-erd@53195cc

commit: 53195cc

@dqn
dqn marked this pull request as ready for review September 10, 2026 04:30
@dqn
dqn requested review from a team as code owners September 10, 2026 04:30
@dqn
dqn requested a review from toiroakr September 10, 2026 04:30
`${RESOLVER}import { process } from "./pipeline";\nexport const run = () => process();`,
RULE,
);
expectClean(`${RESOLVER}export const value = config.process.module;`, RULE);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The README states that a reference reaching a Node-only global through globalThis.process is intentionally not reported (README.md:287-288), and isValueReference() (lib/ast.ts) implements this by treating the property position of a non-computed MemberExpression as a non-value-reference.

That helper is shared across rules, so a future change to it could silently break this guarantee without any test failing.

Could you add a regression test for this case here, e.g.:

expectClean(`${RESOLVER}export const region = globalThis.process.env.REGION;`, RULE);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 53195cc.

@toiroakr toiroakr left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM overall — see inline comment for a minor test-coverage nit.

@toiroakr toiroakr assigned dqn and unassigned toiroakr Sep 10, 2026
Base automatically changed from feat/eslint-plugin-workflow-rules-g1 to main September 10, 2026 05:30
The README documents that a reference reaching a Node-only global through
`globalThis` is not reported, but no test pinned that behavior, so a change to
the shared `isValueReference()` helper could have silently changed it.
@github-actions

Copy link
Copy Markdown

Code Metrics Report (packages/sdk)

main (06957ca) #2313 (b5db9aa) +/-
Coverage 83.5% 83.5% -0.1%
Code to Test Ratio 1:0.5 1:0.5 +0.0
Details
  |                    | main (06957ca) | #2313 (b5db9aa) |  +/-  |
  |--------------------|----------------|-----------------|-------|
- | Coverage           |          83.5% |           83.5% | -0.1% |
  |   Files            |            512 |             512 |     0 |
  |   Lines            |          20664 |           20653 |   -11 |
- |   Covered          |          17271 |           17260 |   -11 |
+ | Code to Test Ratio |          1:0.5 |           1:0.5 |  +0.0 |
  |   Code             |         157022 |          156983 |   -39 |
+ |   Test             |          82289 |           82293 |    +4 |

Code coverage of files in pull request scope (100.0% → 100.0%)

Files Coverage +/- Status
packages/sdk/src/utils/node-builtins.ts 100.0% 0.0% modified

SDK Configure Bundle Size

main (06957ca) #2313 (b5db9aa) +/-
configure-index-size 13.33KB 13.33KB 0KB
dependency-chunks-size 16.06KB 16.06KB 0KB
total-bundle-size 29.39KB 29.39KB 0KB

Runtime Performance

main (06957ca) #2313 (b5db9aa) +/-
Generate Median 2,666ms 1,621ms -1,045ms
Generate Max 2,739ms 1,657ms -1,082ms
Apply Build Median 2,794ms 1,741ms -1,053ms
Apply Build Max 2,854ms 1,913ms -941ms

Type Performance (instantiations)

main (06957ca) #2313 (b5db9aa) +/-
tailordb-basic 47,310 47,310 0
tailordb-optional 4,768 4,768 0
tailordb-relation 4,840 4,840 0
tailordb-validate 694 694 0
tailordb-hooks 5,833 5,833 0
tailordb-object 13,762 13,762 0
tailordb-enum 1,619 1,619 0
resolver-basic 12,120 12,120 0
resolver-nested 35,419 35,419 0
resolver-array 24,056 24,056 0
executor-schedule 4,489 4,489 0
executor-webhook 1,130 1,130 0
executor-record 5,443 5,443 0
executor-resolver 5,808 5,808 0
executor-operation-function 1,118 1,118 0
executor-operation-gql 1,126 1,126 0
executor-operation-webhook 1,137 1,137 0
executor-operation-workflow 1,931 1,931 0

Reported by octocov

@dqn
dqn merged commit 5f2db31 into main Sep 10, 2026
50 checks passed
@dqn
dqn deleted the feat/eslint-plugin-runtime-rules-g2 branch September 10, 2026 10:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants