1'use strict'; 2 3const common = require('../common'); 4const ArrayStream = require('../common/arraystream'); 5const fixtures = require('../common/fixtures'); 6const assert = require('assert'); 7const { builtinModules } = require('module'); 8const publicModules = builtinModules.filter( 9 (lib) => !lib.startsWith('_') && !lib.includes('/'), 10); 11 12if (!common.isMainThread) 13 common.skip('process.chdir is not available in Workers'); 14 15// We have to change the directory to ../fixtures before requiring repl 16// in order to make the tests for completion of node_modules work properly 17// since repl modifies module.paths. 18process.chdir(fixtures.fixturesDir); 19 20const repl = require('repl'); 21 22const putIn = new ArrayStream(); 23const testMe = repl.start({ 24 prompt: '', 25 input: putIn, 26 output: process.stdout, 27 allowBlockingCompletions: true 28}); 29 30// Some errors are passed to the domain, but do not callback 31testMe._domain.on('error', assert.ifError); 32 33// Tab complete provides built in libs for import() 34testMe.complete('import(\'', common.mustCall((error, data) => { 35 assert.strictEqual(error, null); 36 publicModules.forEach((lib) => { 37 assert( 38 data[0].includes(lib) && data[0].includes(`node:${lib}`), 39 `${lib} not found`, 40 ); 41 }); 42 const newModule = 'foobar'; 43 assert(!builtinModules.includes(newModule)); 44 repl.builtinModules.push(newModule); 45 testMe.complete('import(\'', common.mustCall((_, [modules]) => { 46 assert.strictEqual(data[0].length + 1, modules.length); 47 assert(modules.includes(newModule) && 48 !modules.includes(`node:${newModule}`)); 49 })); 50})); 51 52testMe.complete("import\t( 'n", common.mustCall((error, data) => { 53 assert.strictEqual(error, null); 54 assert.strictEqual(data.length, 2); 55 assert.strictEqual(data[1], 'n'); 56 const completions = data[0]; 57 // import(...) completions include `node:` URL modules: 58 publicModules.forEach((lib, index) => 59 assert.strictEqual(completions[index], `node:${lib}`)); 60 assert.strictEqual(completions[publicModules.length], ''); 61 // There is only one Node.js module that starts with n: 62 assert.strictEqual(completions[publicModules.length + 1], 'net'); 63 assert.strictEqual(completions[publicModules.length + 2], ''); 64 // It's possible to pick up non-core modules too 65 completions.slice(publicModules.length + 3).forEach((completion) => { 66 assert.match(completion, /^n/); 67 }); 68})); 69 70{ 71 const expected = ['@nodejsscope', '@nodejsscope/']; 72 // Import calls should handle all types of quotation marks. 73 for (const quotationMark of ["'", '"', '`']) { 74 putIn.run(['.clear']); 75 testMe.complete('import(`@nodejs', common.mustCall((err, data) => { 76 assert.strictEqual(err, null); 77 assert.deepStrictEqual(data, [expected, '@nodejs']); 78 })); 79 80 putIn.run(['.clear']); 81 // Completions should not be greedy in case the quotation ends. 82 const input = `import(${quotationMark}@nodejsscope${quotationMark}`; 83 testMe.complete(input, common.mustCall((err, data) => { 84 assert.strictEqual(err, null); 85 assert.deepStrictEqual(data, [[], undefined]); 86 })); 87 } 88} 89 90{ 91 putIn.run(['.clear']); 92 // Completions should find modules and handle whitespace after the opening 93 // bracket. 94 testMe.complete('import \t("no_ind', common.mustCall((err, data) => { 95 assert.strictEqual(err, null); 96 assert.deepStrictEqual(data, [['no_index', 'no_index/'], 'no_ind']); 97 })); 98} 99 100// Test tab completion for import() relative to the current directory 101{ 102 putIn.run(['.clear']); 103 104 const cwd = process.cwd(); 105 process.chdir(__dirname); 106 107 ['import(\'.', 'import(".'].forEach((input) => { 108 testMe.complete(input, common.mustCall((err, data) => { 109 assert.strictEqual(err, null); 110 assert.strictEqual(data.length, 2); 111 assert.strictEqual(data[1], '.'); 112 assert.strictEqual(data[0].length, 2); 113 assert.ok(data[0].includes('./')); 114 assert.ok(data[0].includes('../')); 115 })); 116 }); 117 118 ['import(\'..', 'import("..'].forEach((input) => { 119 testMe.complete(input, common.mustCall((err, data) => { 120 assert.strictEqual(err, null); 121 assert.deepStrictEqual(data, [['../'], '..']); 122 })); 123 }); 124 125 ['./', './test-'].forEach((path) => { 126 [`import('${path}`, `import("${path}`].forEach((input) => { 127 testMe.complete(input, common.mustCall((err, data) => { 128 assert.strictEqual(err, null); 129 assert.strictEqual(data.length, 2); 130 assert.strictEqual(data[1], path); 131 assert.ok(data[0].includes('./test-repl-tab-complete.js')); 132 })); 133 }); 134 }); 135 136 ['../parallel/', '../parallel/test-'].forEach((path) => { 137 [`import('${path}`, `import("${path}`].forEach((input) => { 138 testMe.complete(input, common.mustCall((err, data) => { 139 assert.strictEqual(err, null); 140 assert.strictEqual(data.length, 2); 141 assert.strictEqual(data[1], path); 142 assert.ok(data[0].includes('../parallel/test-repl-tab-complete.js')); 143 })); 144 }); 145 }); 146 147 { 148 const path = '../fixtures/repl-folder-extensions/f'; 149 testMe.complete(`import('${path}`, common.mustSucceed((data) => { 150 assert.strictEqual(data.length, 2); 151 assert.strictEqual(data[1], path); 152 assert.ok(data[0].includes( 153 '../fixtures/repl-folder-extensions/foo.js/')); 154 })); 155 } 156 157 process.chdir(cwd); 158} 159