Skip to content

Commit

Permalink
repl: add auto‑completion for dynamic import calls
Browse files Browse the repository at this point in the history
  • Loading branch information
ExE-Boss committed Feb 2, 2021
1 parent b5f5c46 commit 5a47430
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 3 deletions.
6 changes: 4 additions & 2 deletions lib/internal/modules/esm/get_format.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const { extname } = require('path');
const { getOptionValue } = require('internal/options');

const experimentalJsonModules = getOptionValue('--experimental-json-modules');
const experimentalSpeciferResolution =
const experimentalSpecifierResolution =
getOptionValue('--experimental-specifier-resolution');
const experimentalWasmModules = getOptionValue('--experimental-wasm-modules');
const { getPackageType } = require('internal/modules/esm/resolve');
Expand Down Expand Up @@ -62,7 +62,7 @@ function defaultGetFormat(url, context, defaultGetFormatUnused) {
format = extensionFormatMap[ext];
}
if (!format) {
if (experimentalSpeciferResolution === 'node') {
if (experimentalSpecifierResolution === 'node') {
process.emitWarning(
'The Node.js specifier resolution in ESM is experimental.',
'ExperimentalWarning');
Expand All @@ -76,3 +76,5 @@ function defaultGetFormat(url, context, defaultGetFormatUnused) {
return { format: null };
}
exports.defaultGetFormat = defaultGetFormat;
exports.extensionFormatMap = extensionFormatMap;
exports.legacyExtensionFormatMap = legacyExtensionFormatMap;
63 changes: 62 additions & 1 deletion lib/repl.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ const {
ArrayPrototypePush,
ArrayPrototypeReverse,
ArrayPrototypeShift,
ArrayPrototypeSlice,
ArrayPrototypeSort,
ArrayPrototypeSplice,
ArrayPrototypeUnshift,
Expand Down Expand Up @@ -125,6 +126,8 @@ let _builtinLibs = ArrayPrototypeFilter(
CJSModule.builtinModules,
(e) => !StringPrototypeStartsWith(e, '_') && !StringPrototypeIncludes(e, '/')
);
const nodeSchemeBuiltinLibs = ArrayPrototypeMap(
_builtinLibs, (lib) => `node:${lib}`);
const domain = require('domain');
let debug = require('internal/util/debuglog').debuglog('repl', (fn) => {
debug = fn;
Expand Down Expand Up @@ -170,6 +173,10 @@ const {
} = internalBinding('contextify');

const history = require('internal/repl/history');
const {
extensionFormatMap,
legacyExtensionFormatMap,
} = require('internal/modules/esm/get_format');

let nextREPLResourceNumber = 1;
// This prevents v8 code cache from getting confused and using a different
Expand Down Expand Up @@ -1104,10 +1111,12 @@ REPLServer.prototype.setPrompt = function setPrompt(prompt) {
ReflectApply(Interface.prototype.setPrompt, this, [prompt]);
};

const importRE = /\bimport\s*\(\s*['"`](([\w@./-]+\/)?(?:[\w@./-:]*))(?![^'"`])$/;
const requireRE = /\brequire\s*\(\s*['"`](([\w@./-]+\/)?(?:[\w@./-]*))(?![^'"`])$/;
const fsAutoCompleteRE = /fs(?:\.promises)?\.\s*[a-z][a-zA-Z]+\(\s*["'](.*)/;
const simpleExpressionRE =
/(?:[a-zA-Z_$](?:\w|\$)*\??\.)*[a-zA-Z_$](?:\w|\$)*\??\.?$/;
const versionedFileNamesRe = /-\d+\.\d+/;

function isIdentifier(str) {
if (str === '') {
Expand Down Expand Up @@ -1214,7 +1223,6 @@ function complete(line, callback) {
const indexes = ArrayPrototypeMap(extensions,
(extension) => `index${extension}`);
ArrayPrototypePush(indexes, 'package.json', 'index');
const versionedFileNamesRe = /-\d+\.\d+/;

const match = StringPrototypeMatch(line, requireRE);
completeOn = match[1];
Expand Down Expand Up @@ -1269,6 +1277,59 @@ function complete(line, callback) {
if (!subdir) {
ArrayPrototypePush(completionGroups, _builtinLibs);
}
} else if (RegExpPrototypeTest(importRE, line) &&
this.allowBlockingCompletions) {
// import('...<Tab>')
// File extensions that can be imported:
const extensions =
getOptionValue('--experimental-specifier-resolution') === 'node' ?
ObjectKeys(legacyExtensionFormatMap) :
ObjectKeys(extensionFormatMap);

const match = StringPrototypeMatch(line, importRE);
completeOn = match[1];
const subdir = match[2] || '';
filter = completeOn;
group = [];
let paths = [];
if (completeOn === '.') {
group = ['./', '../'];
} else if (completeOn === '..') {
group = ['../'];
} else if (RegExpPrototypeTest(/^\.\.?\//, completeOn)) {
paths = [process.cwd()];
} else {
paths = ArrayPrototypeSlice(module.paths);
}

ArrayPrototypeForEach(paths, (dir) => {
dir = path.resolve(dir, subdir);
const dirents = gracefulReaddir(dir, { withFileTypes: true }) || [];
for (const dirent of dirents) {
const { name } = dirent;
if (RegExpPrototypeTest(versionedFileNamesRe, name) ||
name === '.npm') {
// Exclude versioned names that 'npm' installs.
continue;
}
const extension = path.extname(name);
if (!dirent.isDirectory()) {
if (StringPrototypeIncludes(extensions, extension) && !subdir) {
ArrayPrototypePush(group, `${subdir}${name}`);
}
continue;
}
ArrayPrototypePush(group, `${subdir}${name}/`);
}
});

if (group.length) {
ArrayPrototypePush(completionGroups, group);
}

if (!subdir) {
ArrayPrototypePush(completionGroups, nodeSchemeBuiltinLibs, _builtinLibs);
}
} else if (RegExpPrototypeTest(fsAutoCompleteRE, line) &&
this.allowBlockingCompletions) {
({ 0: completionGroups, 1: completeOn } = completeFSFunctions(line));
Expand Down

0 comments on commit 5a47430

Please sign in to comment.