From 15b543ace415c8c5848bbf50aac758bf94d4ce09 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Mon, 18 Dec 2023 16:05:43 -0800 Subject: [PATCH 1/3] feat(eslint-plugin)!: Disallow ?? and ?. operators --- .../eslint-plugin/lib/configs/recommended.js | 5 ++ .../lib/rules/no-nullish-coalescing.js | 35 ++++++++++++ .../lib/rules/no-optional-chaining.js | 55 +++++++++++++++++++ 3 files changed, 95 insertions(+) create mode 100644 packages/eslint-plugin/lib/rules/no-nullish-coalescing.js create mode 100644 packages/eslint-plugin/lib/rules/no-optional-chaining.js diff --git a/packages/eslint-plugin/lib/configs/recommended.js b/packages/eslint-plugin/lib/configs/recommended.js index f0dba77915..cde8ed21b3 100644 --- a/packages/eslint-plugin/lib/configs/recommended.js +++ b/packages/eslint-plugin/lib/configs/recommended.js @@ -65,5 +65,10 @@ module.exports = { rules: { '@endo/assert-fail-as-throw': 'error', 'guard-for-in': 'error', + // Agoric still uses Endo dependencies under an emulation of ESM we call RESM + // because it is invoked with `node -r esm`. + // RESM does not support ?? nor ?. operators, so we must avoid them expressly. + '@endo/no-optional-chaining': 'error', + '@endo/no-nullish-coalescing': 'error', }, }; diff --git a/packages/eslint-plugin/lib/rules/no-nullish-coalescing.js b/packages/eslint-plugin/lib/rules/no-nullish-coalescing.js new file mode 100644 index 0000000000..4bc6b223f6 --- /dev/null +++ b/packages/eslint-plugin/lib/rules/no-nullish-coalescing.js @@ -0,0 +1,35 @@ +'use strict'; + +// Agoric still uses Endo dependencies under an emulation of ESM we call RESM +// because it is invoked with `node -r esm`. +// RESM does not support ?? nor ?. operators, so we must avoid them expressly. +// TODO remove when https://github.com/Agoric/agoric-sdk/issues/8671 + +module.exports = { + meta: { + docs: { + description: 'disallow nullish coalescing.', + category: 'ES2020', + recommended: false, + url: 'https://github.com/endojs/endo/blob/master/packages/eslint-plugin/lib/rules/no-nullish-coalescing.js', + }, + fixable: null, + messages: { + forbidden: 'ES2020 nullish coalescing is forbidden.', + }, + schema: [], + type: 'problem', + }, + create(context) { + return { + LogicalExpression(node) { + if (node.operator === '??') { + context.report({ + node, + messageId: 'forbidden', + }); + } + }, + }; + }, +}; diff --git a/packages/eslint-plugin/lib/rules/no-optional-chaining.js b/packages/eslint-plugin/lib/rules/no-optional-chaining.js new file mode 100644 index 0000000000..7ec6355ffe --- /dev/null +++ b/packages/eslint-plugin/lib/rules/no-optional-chaining.js @@ -0,0 +1,55 @@ +/** @author Yosuke Ota */ +'use strict'; + +// Agoric still uses Endo dependencies under an emulation of ESM we call RESM +// because it is invoked with `node -r esm`. +// RESM does not support ?? nor ?. operators, so we must avoid them expressly. +// TODO remove when https://github.com/Agoric/agoric-sdk/issues/8671 + +module.exports = { + meta: { + docs: { + description: 'disallow optional chaining.', + category: 'ES2020', + recommended: false, + url: 'http://mysticatea.github.io/eslint-plugin-es/rules/no-optional-chaining.html', + }, + fixable: null, + messages: { + forbidden: 'ES2020 optional chaining is forbidden.', + }, + schema: [], + type: 'problem', + }, + create(context) { + const sourceCode = context.getSourceCode(); + + /** + * @param {Token} token The token to check. + * @returns {boolean} whether the token is a `?.` token. + */ + function isQuestionDotToken(token) { + return ( + token.value === '?.' && + (token.type === 'Punctuator' || // espree has been parsed well. + // espree@7.1.0 doesn't parse "?." tokens well. Therefore, get the string from the source code and check it. + sourceCode.getText(token) === '?.') + ); + } + + return { + 'CallExpression[optional=true]'(node) { + context.report({ + node: sourceCode.getTokenAfter(node.callee, isQuestionDotToken), + messageId: 'forbidden', + }); + }, + 'MemberExpression[optional=true]'(node) { + context.report({ + node: sourceCode.getTokenAfter(node.object, isQuestionDotToken), + messageId: 'forbidden', + }); + }, + }; + }, +}; From 9c2445eba86b0f6d2dee00d1f66e94df420924cb Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Mon, 18 Dec 2023 16:06:16 -0800 Subject: [PATCH 2/3] fix(evasive-transform): Do not use ?? nor ?. operators --- packages/evasive-transform/src/generate.js | 4 +++- packages/evasive-transform/src/index.js | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/evasive-transform/src/generate.js b/packages/evasive-transform/src/generate.js index c946f7661c..a00bd2df15 100644 --- a/packages/evasive-transform/src/generate.js +++ b/packages/evasive-transform/src/generate.js @@ -59,7 +59,9 @@ const { default: generator } = /** @type {any} */ (babelGenerate); export const generate = /** @type {GenerateAstWithSourceMap & GenerateAstWithoutSourceMap} */ ( (ast, options) => { - const sourceUrl = options?.sourceUrl; + // TODO Use options?.sourceUrl when resolved: + // https://github.com/Agoric/agoric-sdk/issues/8671 + const sourceUrl = options ? options.sourceUrl : undefined; const result = generator(ast, { sourceFileName: sourceUrl, sourceMaps: Boolean(sourceUrl), diff --git a/packages/evasive-transform/src/index.js b/packages/evasive-transform/src/index.js index 75d02f72f5..da4b05eae0 100644 --- a/packages/evasive-transform/src/index.js +++ b/packages/evasive-transform/src/index.js @@ -35,7 +35,9 @@ import { generate } from './generate.js'; * @public */ export async function evadeCensor(source, options) { - const { sourceMap, sourceUrl, useLocationUnmap, sourceType } = options ?? {}; + // TODO Use options ?? {} when resolved: + // https://github.com/Agoric/agoric-sdk/issues/8671 + const { sourceMap, sourceUrl, useLocationUnmap, sourceType } = options || {}; // See "Chesterton's Fence" await null; From 3355c044dbf461ad52045c0b380917726f954aa7 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Mon, 18 Dec 2023 16:09:40 -0800 Subject: [PATCH 3/3] fix(bundle-source): Avoid ?? operator for RESM --- packages/bundle-source/src/is-entrypoint.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/bundle-source/src/is-entrypoint.js b/packages/bundle-source/src/is-entrypoint.js index ce72b4ac26..58790eede1 100644 --- a/packages/bundle-source/src/is-entrypoint.js +++ b/packages/bundle-source/src/is-entrypoint.js @@ -3,7 +3,14 @@ import url from 'url'; import process from 'process'; import fs from 'fs'; +// Agoric still uses Endo dependencies under an emulation of ESM we call RESM +// because it is invoked with `node -r esm`. +// RESM does not support ?? nor ?. operators, so we must avoid them expressly. +// TODO remove when https://github.com/Agoric/agoric-sdk/issues/8671 +const favor = (primary, secondary) => + primary === undefined ? secondary : primary; + // FIXME: Should maybe be exported by '@endo/something'? export const isEntrypoint = href => String(href) === - url.pathToFileURL(fs.realpathSync(process.argv[1]) ?? '/').href; + url.pathToFileURL(fs.realpathSync(favor(process.argv[1]), '/')).href;