Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug Typescript Nigtly .js moduleResolve nodenext - usage of require not detected properly #48716

Closed
frank-dspeed opened this issue Apr 15, 2022 · 6 comments
Assignees
Labels
Bug A bug in TypeScript

Comments

@frank-dspeed
Copy link

frank-dspeed commented Apr 15, 2022

Bug Report

πŸ”Ž Search Terms

require not used

πŸ•— Version & Regression Information

  • I was unable to test this on prior versions because not needed

⏯ Playground Link

Playground link with relevant code

πŸ’» Code

import module from 'module';
/**
 * Wrapper that exposes the CJS version of the bindings
 * to get imported in ESM 
 * @deprecated use import('node-cblite/cblite.js)
 */
const require = module.createRequire(import.meta.url);
const cblite = require('./binding.cjs');
export { cblite };

πŸ™ Actual behavior

src/native/binding.js:7:7 - error TS6133: 'require' is declared but its value is never read.

7 const require = module.createRequire(import.meta.url);

πŸ™‚ Expected behavior

require is used it can be fixed via renaming it to Require with CamelCase

i guess this is part of the nodenext resolve and that js does not preserve the require call and will translate it even when it does not come from the nativ require so require is some how now a reserved word in .js files which is not expected! as they are modules defined by package.json type module

this is even more evil when you target other environments that also got a own require implementation no matter which one require-js or the graaljs engine the deno engine anything running on raw v8 that implements require,

require is a relativ common function name in many environments and does not match the NodeJS.Require type

Solution

When moduleResolution: nodenext + target: ESNext that also implicit sets preserve require in all current nodejs versions and environments require should stay require because it only exists for backward compat and is most time always a shim as we produce nodeNext from ESNext for ESNext

		"module": "ESNext",
		"target": "ESNext",
		"moduleResolution": "nodenext",

and yes there are 2 package.json near that one in the project dir and one even in the outer ../

both similar whole content

{ "type": "module" }
@andrewbranch andrewbranch added the Needs More Info The issue still hasn't been fully clarified label Apr 21, 2022
@andrewbranch
Copy link
Member

I can’t reproduce this. Please provide a full repro.

@andrewbranch andrewbranch added Bug A bug in TypeScript and removed Needs More Info The issue still hasn't been fully clarified labels Apr 21, 2022
@andrewbranch
Copy link
Member

andrewbranch commented Apr 21, 2022

Ah, the key piece of info is that this is a JS file. In JS we seem to assume any invocation of require() is the one injected into CJS modules, whether or not there is something shadowing it in scope and whether or not you’re even in a CJS module. Yikes.

@andrewbranch
Copy link
Member

It seems likely this has the same root cause / is basically the same bug as #48726

@frank-dspeed
Copy link
Author

frank-dspeed commented Apr 22, 2022

I guess your correct i code a lot of low level stuff so i code it in CJS as you know maybe there is no access to native code directly inside ESM. All the import magic is the work of the cjs-module-lexer that trys to create namedExports for the native code and CJS modules.

The engine it self is always CJS no matter if you even do node my.mjs node stays CJS as the C Code that creates the Environment is Sync by default for good reason.

when we do require('module') we get directly the memory pointer to the module C code and the createRequire can be used many times with diffrent path as you sayed

It is localy scoped shadowing the global.

but i also spotted many diffrent small issues while i am coding with real scopes
my.cjs

'use static' // first line entrypoint 

{

  // Creating Scope this is now not Global
 const m = require('module'); // line 1: in my.cjs comes from globalThis
 const require = m.createRequire(); // line 2: in my.cjs 
 globalThis.MyNewModule = require() // line 3: in my.cjs the sideEffect Based globalExport.
 // line 4: End of File Empty line handled as ```  ;  ``` in parsing so all other modules required after this will not have a new require they still use old assigned still to globalThis.require
};

that is the code that really is written into any .CJS file .cjs files are not handled like .js files nodejs did also restrict for example: SyntaxError: Block-scoped declarations (let, const, function, class) not yet supported outside strict mode

thats why .CJS default to strict mode but it is documented no where. In general the module lexer will choose if ti is JS CJS or ESM

so while there are only 2 module systems there are in reality 3 as useStrict is also a Module System that is able to register globals via sideEffects. the old JQuery Module system i call it that way because thats where all did use it and saw it in action most of the time.

Extra Bonus

The Typescript Checker aka Program out of history needs to create 3 isolated scopes 2 follow sync processing rules and 1 following async only including order resolution.

in my universal Module System i have a lot of filds that i need to neogate module order.

        // {
        //   code: string,                  // the generated JS code
        //   dynamicImports: string[],      // external modules imported dynamically by the chunk
        //   exports: string[],             // exported variable names
        //   facadeModuleId: string | null, // the id of a module that this chunk corresponds to
        //   fileName: string,              // the chunk file name
        //   implicitlyLoadedBefore: string[]; // entries that should only be loaded after this chunk
        //   imports: string[],             // external modules imported statically by the chunk
        //   importedBindings: {[imported: string]: string[]} // imported bindings per dependency
        //   isDynamicEntry: boolean,       // is this chunk a dynamic entry point
        //   isEntry: boolean,              // is this chunk a static entry point
        //   isImplicitEntry: boolean,      // should this chunk only be loaded after other chunks
        //   map: string | null,            // sourcemaps if present
        //   modules: {                     // information about the modules in this chunk
        //     [id: string]: {
        //       renderedExports: string[]; // exported variable names that were included
        //       removedExports: string[];  // exported variable names that were removed
        //       renderedLength: number;    // the length of the remaining code in this module
        //       originalLength: number;    // the original length of the code in this module
        //       code: string | null;       // remaining code in this module
        //     };
        //   },
        //   name: string                   // the name of this chunk as used in naming patterns
        //   referencedFiles: string[]      // files referenced via import.meta.ROLLUP_FILE_URL_<id>
        //   type: 'chunk',                 // signifies that this is a chunk
        // }

after that general file loading neogation comes the scope evaluation there we read all the code but abstracted away the general Module Systems like CJS and ESM now it is only JS and we need to look how we run that JS in its correct Scope so that we know where the results are like globalThis assignment or if var got used in a file loaded before .cjs and so on the whole code ast needs to get interpreted into its runtime version.

short

i wanted to point out that maybe the whole scope system needs a rework for example use stric also removes octal literals and so on while ES6 introduces to write them with the 0o58 === 058 // in none strict mode in strict mode it is not eq as there 058 is not allowed 8 is invalid octal the 0o58 is the only one that works in all contexts. when they are at last es6 es2015

the octal and require values all this is out of my view type related and the resulting type is scope related but typescript is not aware about real global scope and its code state at a given time so it can not infer the correct type or produce the correct type without getting hardcoded at present.

@frank-dspeed
Copy link
Author

@sandersn
Copy link
Member

sandersn commented Aug 3, 2022

Like #48726, I think that the common case is best supported by the current assumption that require is always used for imports. @andrewbranch suggested that future module resolution modes could take into account the use of createRequire and treat it and require specially. This is basically a duplicate of #49726 so I'm going to close this as well; any future developments will be based on work from #50152.

@sandersn sandersn closed this as completed Aug 3, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript
Projects
None yet
Development

No branches or pull requests

3 participants