Skip to content

Commit

Permalink
module,win: fix long path resolve
Browse files Browse the repository at this point in the history
  • Loading branch information
huseyinacacak-janea committed Jun 3, 2024
1 parent 063afa8 commit 27a4505
Show file tree
Hide file tree
Showing 6 changed files with 231 additions and 50 deletions.
16 changes: 16 additions & 0 deletions src/node_file.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2989,9 +2989,15 @@ constexpr std::array<std::string_view, 10> legacy_main_extensions = {
".js",
".json",
".node",
#ifdef _WIN32
"\\index.js",
"\\index.json",
"\\index.node",
#else
"/index.js",
"/index.json",
"/index.node",
#endif
".js",
".json",
".node"};
Expand Down Expand Up @@ -3038,6 +3044,11 @@ void BindingData::LegacyMainResolve(const FunctionCallbackInfo<Value>& args) {

for (int i = 0; i < legacy_main_extensions_with_main_end; i++) {
file_path = *initial_file_path + std::string(legacy_main_extensions[i]);
// TODO(huseyinacacak-janea): Remove ifdef after
// path.toNamespacedPath logic is ported to C++
#ifdef _WIN32
file_path = "\\\\?\\" + file_path;
#endif

switch (FilePathIsFile(env, file_path)) {
case BindingData::FilePathIsFileReturnType::kIsFile:
Expand Down Expand Up @@ -3075,6 +3086,11 @@ void BindingData::LegacyMainResolve(const FunctionCallbackInfo<Value>& args) {
i < legacy_main_extensions_package_fallback_end;
i++) {
file_path = *initial_file_path + std::string(legacy_main_extensions[i]);
// TODO(huseyinacacak-janea): Remove ifdef after
// path.toNamespacedPath logic is ported to C++
#ifdef _WIN32
file_path = "\\\\?\\" + file_path;
#endif

switch (FilePathIsFile(env, file_path)) {
case BindingData::FilePathIsFileReturnType::kIsFile:
Expand Down
14 changes: 14 additions & 0 deletions src/node_modules.cc
Original file line number Diff line number Diff line change
Expand Up @@ -324,8 +324,15 @@ void BindingData::GetNearestParentPackageJSON(

Realm* realm = Realm::GetCurrent(args);
Utf8Value path_value(realm->isolate(), args[0]);
// TODO(huseyinacacak-janea): Remove ifdef after
// path.toNamespacedPath logic is ported to C++
#ifdef _WIN32
auto package_json = TraverseParent(
realm, std::filesystem::path("\\\\?\\" + path_value.ToString()));
#else
auto package_json =
TraverseParent(realm, std::filesystem::path(path_value.ToString()));
#endif

if (package_json != nullptr) {
args.GetReturnValue().Set(package_json->Serialize(realm));
Expand All @@ -339,8 +346,15 @@ void BindingData::GetNearestParentPackageJSONType(

Realm* realm = Realm::GetCurrent(args);
Utf8Value path(realm->isolate(), args[0]);
// TODO(huseyinacacak-janea): Remove ifdef after
// path.toNamespacedPath logic is ported to C++
#ifdef _WIN32
auto package_json =
TraverseParent(realm, std::filesystem::path("\\\\?\\" + path.ToString()));
#else
auto package_json =
TraverseParent(realm, std::filesystem::path(path.ToString()));
#endif

if (package_json == nullptr) {
return;
Expand Down
42 changes: 0 additions & 42 deletions test/es-module/test-GH-50753.js

This file was deleted.

2 changes: 1 addition & 1 deletion test/es-module/test-cjs-esm-warn.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const { describe, it } = require('node:test');

const requiringCjsAsEsm = path.resolve(fixtures.path('/es-modules/cjs-esm.js'));
const requiringEsm = path.resolve(fixtures.path('/es-modules/cjs-esm-esm.js'));
const pjson = path.resolve(
const pjson = path.toNamespacedPath(
fixtures.path('/es-modules/package-type-module/package.json')
);

Expand Down
18 changes: 11 additions & 7 deletions test/es-module/test-cjs-legacyMainResolve-permission.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,11 @@ describe('legacyMainResolve', () => {
assert.throws(() => legacyMainResolve(packageJsonUrl, packageConfig, base), {
code: 'ERR_ACCESS_DENIED',
resource: path.resolve(
${JSON.stringify(fixtextureFolderEscaped)},
${JSON.stringify(mainOrFolder)},
resource: path.toNamespacedPath(
path.resolve(
${JSON.stringify(fixtextureFolderEscaped)},
${JSON.stringify(mainOrFolder)},
)
)
});
`,
Expand Down Expand Up @@ -120,10 +122,12 @@ describe('legacyMainResolve', () => {
assert.throws(() => legacyMainResolve(packageJsonUrl, packageConfig, base), {
code: 'ERR_ACCESS_DENIED',
resource: path.resolve(
${JSON.stringify(fixtextureFolderEscaped)},
${JSON.stringify(folder)},
${JSON.stringify(expectedFile)},
resource: path.toNamespacedPath(
path.resolve(
${JSON.stringify(fixtextureFolderEscaped)},
${JSON.stringify(folder)},
${JSON.stringify(expectedFile)},
)
)
});
`,
Expand Down
189 changes: 189 additions & 0 deletions test/es-module/test-esm-long-path-win.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
'use strict';

// Flags: --expose-internals

const common = require('../common');
if (!common.isWindows) {
common.skip('this test is Windows-specific.');
}

const fs = require('fs');
const path = require('path');
const tmpdir = require('../common/tmpdir');
const { describe, it } = require('node:test');
const { legacyMainResolve } = require('node:internal/modules/esm/resolve');
const { pathToFileURL } = require('node:url');
const { spawnPromisified } = require('../common');
const assert = require('assert');
const { execPath } = require('node:process');

describe('long path on Windows', () => {
it('check long path in ReadPackageJSON', () => {
// Module layout will be the following:
// package.json
// main
// main.js
const packageDirNameLen = Math.max(260 - tmpdir.path.length, 1);
const packageDirName = tmpdir.resolve('x'.repeat(packageDirNameLen));
const packageDirPath = path.resolve(packageDirName);
const packageJsonFilePath = path.join(packageDirPath, 'package.json');
const mainDirName = 'main';
const mainDirPath = path.resolve(packageDirPath, mainDirName);
const mainJsFile = 'main.js';
const mainJsFilePath = path.resolve(mainDirPath, mainJsFile);

tmpdir.refresh();

fs.mkdirSync(packageDirPath);
fs.writeFileSync(
packageJsonFilePath,
`{"main":"${mainDirName}/${mainJsFile}"}`
);
fs.mkdirSync(mainDirPath);
fs.writeFileSync(mainJsFilePath, 'console.log("hello world");');

require('internal/modules/run_main').executeUserEntryPoint(packageDirPath);

tmpdir.refresh();
});

it('check long path in LegacyMainResolve - 1', () => {
// Module layout will be the following:
// package.json
// index.js
const packageDirNameLen = Math.max(260 - tmpdir.path.length, 1);
const packageDirPath = tmpdir.resolve('y'.repeat(packageDirNameLen));
const packageJSPath = path.join(packageDirPath, 'package.json');
const indexJSPath = path.join(packageDirPath, 'index.js');
const packageConfig = { };

tmpdir.refresh();

fs.mkdirSync(packageDirPath);
fs.writeFileSync(packageJSPath, '');
fs.writeFileSync(indexJSPath, '');

const packageJsonUrl = pathToFileURL(
path.resolve(packageJSPath)
);

console.log(legacyMainResolve(packageJsonUrl, packageConfig, ''));
});

it('check long path in LegacyMainResolve - 2', () => {
// Module layout will be the following:
// package.json
// main.js
const packageDirNameLen = Math.max(260 - tmpdir.path.length, 1);
const packageDirPath = tmpdir.resolve('z'.repeat(packageDirNameLen));
const packageJSPath = path.join(packageDirPath, 'package.json');
const indexJSPath = path.join(packageDirPath, 'main.js');
const packageConfig = { main: 'main.js' };

tmpdir.refresh();

fs.mkdirSync(packageDirPath);
fs.writeFileSync(packageJSPath, '');
fs.writeFileSync(indexJSPath, '');

const packageJsonUrl = pathToFileURL(
path.resolve(packageJSPath)
);

console.log(legacyMainResolve(packageJsonUrl, packageConfig, ''));
});

it('check long path in GetNearestParentPackageJSON', async () => {
// Module layout will be the following:
// node_modules
// test-module
// package.json (path is less than 260 chars long)
// cjs
// package.json (path is more than 260 chars long)
// index.js
const testModuleDirPath = 'node_modules/test-module';
let cjsDirPath = path.join(testModuleDirPath, 'cjs');
let modulePackageJSPath = path.join(testModuleDirPath, 'package.json');
let cjsPackageJSPath = path.join(cjsDirPath, 'package.json');
let cjsIndexJSPath = path.join(cjsDirPath, 'index.js');

const tmpDirNameLen = Math.max(
260 - tmpdir.path.length - cjsPackageJSPath.length, 1);
const tmpDirPath = tmpdir.resolve('k'.repeat(tmpDirNameLen));

cjsDirPath = path.join(tmpDirPath, cjsDirPath);
modulePackageJSPath = path.join(tmpDirPath, modulePackageJSPath);
cjsPackageJSPath = path.join(tmpDirPath, cjsPackageJSPath);
cjsIndexJSPath = path.join(tmpDirPath, cjsIndexJSPath);

tmpdir.refresh();

fs.mkdirSync(cjsDirPath, { recursive: true });
fs.writeFileSync(
modulePackageJSPath,
`{
"type": "module"
}`
);
fs.writeFileSync(
cjsPackageJSPath,
`{
"type": "commonjs"
}`
);
fs.writeFileSync(
cjsIndexJSPath,
'const fs = require("fs");'
);
const { code, signal, stderr } = await spawnPromisified(execPath, [cjsIndexJSPath]);
assert.strictEqual(stderr.trim(), '');
assert.strictEqual(code, 0);
assert.strictEqual(signal, null);
});

it('check long path in GetNearestParentPackageJSONType', async () => {
// Module layout will be the following:
// node_modules
// test-module
// package.json (path is less than 260 chars long)
// cjs
// package.json (path is more than 260 chars long)
// index.js
const testModuleDirPath = 'node_modules/test-module';
let cjsDirPath = path.join(testModuleDirPath, 'cjs');
let modulePackageJSPath = path.join(testModuleDirPath, 'package.json');
let cjsPackageJSPath = path.join(cjsDirPath, 'package.json');
let cjsIndexJSPath = path.join(cjsDirPath, 'index.js');

const tmpDirNameLen = Math.max(260 - tmpdir.path.length - cjsPackageJSPath.length, 1);
const tmpDirPath = tmpdir.resolve('l'.repeat(tmpDirNameLen));

cjsDirPath = path.join(tmpDirPath, cjsDirPath);
modulePackageJSPath = path.join(tmpDirPath, modulePackageJSPath);
cjsPackageJSPath = path.join(tmpDirPath, cjsPackageJSPath);
cjsIndexJSPath = path.join(tmpDirPath, cjsIndexJSPath);

tmpdir.refresh();

fs.mkdirSync(cjsDirPath, { recursive: true });
fs.writeFileSync(
modulePackageJSPath,
`{
"type": "module"
}`
);
fs.writeFileSync(
cjsPackageJSPath,
`{
"type": "commonjs"
}`
);

fs.writeFileSync(cjsIndexJSPath, 'import fs from "node:fs/promises";');
const { code, signal, stderr } = await spawnPromisified(execPath, [cjsIndexJSPath]);

assert.ok(stderr.includes('Warning: To load an ES module'));
assert.strictEqual(code, 1);
assert.strictEqual(signal, null);
});
});

0 comments on commit 27a4505

Please sign in to comment.