Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* ESLint config for the Dataform CLI & Core repo.
*
* Scope: currently only used to enforce sandbox-safety rules in `core/`.
* The rest of the codebase is still linted by tslint (see tslint.json).
* Add ESLint rules here only when they need capabilities tslint 5.17 cannot
* express (per-file `overrides`, per-message text, AST rules, etc.).
*/
module.exports = {
root: true,
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 2020,
sourceType: 'module',
},
overrides: [
{
// core/ runs inside the V8 compilation sandbox — no Node built-ins.
files: ['core/**/*.ts'],
rules: {
'no-node-builtins': 'error',
},
},
{
// Tests under core/ run on the host Node runtime, not in the sandbox,
// so they may legitimately use fs, path, etc. for fixture setup.
files: ['core/**/*_test.ts', 'core/**/*.test.ts'],
rules: {
'no-node-builtins': 'off',
},
},
],
};
78 changes: 78 additions & 0 deletions eslint-rules/no-node-builtins.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/**
* Custom ESLint rule: disallow Node.js built-in imports in code that runs
* inside the Dataform V8 compilation sandbox (i.e. `core/`).
*
* The blocked set is generated at rule load time from Node's own
* `require('module').builtinModules`, so any built-in Node adds later is
* caught automatically. Both bare (`path`) and prefixed (`node:path`) forms
* are covered.
*/
'use strict';

const { builtinModules } = require('module');

const blocked = new Set([
...builtinModules,
...builtinModules.map((m) => `node:${m}`),
]);

function report(context, node, name) {
context.report({
node,
messageId: 'nodeBuiltin',
data: { name },
});
}

module.exports = {
meta: {
type: 'problem',
docs: {
description:
'Disallow Node.js built-in imports in Dataform V8 sandbox code.',
},
schema: [],
messages: {
nodeBuiltin:
"The Node.js built-in module '{{name}}' is not available in the " +
'Dataform V8 compilation sandbox. Move code that requires Node ' +
'built-ins to cli/, which runs on the host Node.js runtime and does ' +
'not have this restriction.',
},
},
create(context) {
return {
// import x from 'path' | import * as x from 'path' | import 'path'
ImportDeclaration(node) {
const name = node.source.value;
if (blocked.has(name)) report(context, node, name);
},
// import x = require('path')
TSImportEqualsDeclaration(node) {
const ref = node.moduleReference;
if (
ref &&
ref.type === 'TSExternalModuleReference' &&
ref.expression &&
typeof ref.expression.value === 'string' &&
blocked.has(ref.expression.value)
) {
report(context, node, ref.expression.value);
}
},
// require('path') or require('node:path')
CallExpression(node) {
if (
node.callee.type === 'Identifier' &&
node.callee.name === 'require' &&
node.arguments.length === 1 &&
node.arguments[0].type === 'Literal' &&
typeof node.arguments[0].value === 'string' &&
blocked.has(node.arguments[0].value)
) {
report(context, node, node.arguments[0].value);
}
},
};
},
};
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,14 @@
"@types/tmp": "^0.2.0",
"@types/vscode": "^1.45.1",
"@types/yargs": "^15.0.5",
"@typescript-eslint/parser": "^5.62.0",
"chai": "^4.2.0",
"chalk": "^4.0.0",
"chokidar": "^3.5.3",
"deepmerge": "^4.2.2",
"diff": "^8.0.3",
"escodegen": "^1.13.0",
"eslint": "^8.57.0",
"espree": "^7.0.0",
"estraverse": "^5.1.0",
"fs-extra": "^9.0.0",
Expand Down Expand Up @@ -101,6 +103,8 @@
"**/node-fetch": "^2.6.7",
"**/markdown-it": "^12.3.2",
"**/wrap-ansi": "7.0.0",
"**/string-width": "4.1.0"
"**/string-width": "4.1.0",
"**/@eslint-community/eslint-utils": "4.9.1",
"**/@ungap/structured-clone": "1.3.1"
}
}
4 changes: 4 additions & 0 deletions scripts/lint
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
#!/bin/bash

bazel build @npm//tslint/bin:tslint && bazel-bin/external/npm/tslint/bin/tslint.sh --project .

# Run eslint (currently used only to guard core/ against Node built-in
# imports; see .eslintrc.js and eslint-rules/).
bazel build @npm//eslint/bin:eslint && bazel-bin/external/npm/eslint/bin/eslint.sh --rulesdir eslint-rules 'core/**/*.ts'
4 changes: 4 additions & 0 deletions scripts/run_tests
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ set -e
bazel run @nodejs//:yarn
bazel build @npm//tslint/bin:tslint && bazel-bin/external/npm/tslint/bin/tslint.sh --project .

# Run eslint (currently used only to guard core/ against Node built-in
# imports; see .eslintrc.js and eslint-rules/).
bazel build @npm//eslint/bin:eslint && bazel-bin/external/npm/eslint/bin/eslint.sh --rulesdir eslint-rules 'core/**/*.ts'

# Run unit tests
bazel test ... \
--test_tag_filters=-integration \
Expand Down
Loading
Loading