Skip to content

Static and dynamic imports inside loader code should have same behaviour #43244

Description

@davidje13

I have been playing around with the loaders API in NodeJS and found an inconsistency which caused me some problems:

If a loader imports another module using a static import, that import will not be processed through the loader (this wouldn't be possible, so is expected behaviour). However, if the loader uses a dynamic import, that import will be processed by the loader (even if the same import was previously referenced statically).

Example

index.mjs

#!/usr/bin/env node --experimental-loader ./loader.mjs

import a from './imported.mjs';
console.log(a);

imported.mjs

export default '<imported: preprocessing-result-here>';

loader-imported.mjs

export default '<loader-imported: not processed>';

loader.mjs

import { readFile } from 'fs/promises';

import staticImport from './loader-imported.mjs';

export async function load(url, context, defaultLoad) {
  if (url.includes('loader-imported.mjs')) {
    const raw = await readFile(new URL(url).pathname, 'utf-8');
    return { format: 'module', source: raw.replace(/not processed/g, 'processed') };
  }
  if (url.includes('imported.mjs')) {
    const { default: dynamicImport } = await import ('./loader-imported.mjs');
    const raw = await readFile(new URL(url).pathname, 'utf-8');
    return { format: 'module', source: raw.replace(/preprocessing-result-here/g, 'processed with static: ' + staticImport + ', dynamic: ' + dynamicImport) };
  }
  return defaultLoad(url, context, defaultLoad);
}

Result

<imported: processed with static: <loader-imported: not processed>, dynamic: <loader-imported: processed>>

Expectation

For my use-case, I would prefer if loaders were considered entirely separate; never operating on their own imports (regardless of whether they are static or dynamic), i.e.:

<imported: processed with static: <loader-imported: not processed>, dynamic: <loader-imported: not processed>>

For more general context: I am using loaders to apply customisable pre-processing stages during a test run. So for example, if the user wishes to run the code through Babel before executing, the loader will dynamically import Babel and apply it. But they might choose to use TSC instead, or something else, so dynamic imports are used to avoid hard dependencies on tools the user isn't using. Another possible approach would be to have entirely separate loaders per tool but that gets a bit messy.

Metadata

Metadata

Assignees

No one assigned

    Labels

    confirmed-bugIssues with confirmed bugs.loadersIssues and PRs related to ES module loadersmoduleIssues and PRs related to the module subsystem.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions