systemjs / systemjs

Dynamic ES module loader

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

problem to import per dependencies and commonjs

misaku opened this issue · comments

  • SystemJS Version:
  • Which library are you using?
    • system.js
    • s.js
    • system-node.cjs
  • Which extras are you using?
    • AMD extra
    • Named Exports
    • Named Register
    • Transform
    • Use Default
    • Global
    • Dynamic Import Maps
  • Are you using any custom hooks?
    single-spa

Question

i heve few problens with imports cdns.
example i tryed to impotr zustand, but zustand need per dependecies zustand/vanilla, and i import too, but now need per dependencies use-sync-external-store/shim/with-selector and this lib is cjs, becouse it my application is broken.
i would like know if have correct import with automatic dependencies and how can i solve problem with cjs?

@misaku suggest you to use the jspm, here is my poc, after the plugin loaded you can just System.import('npm:zustand')

npm resolver

const orig = (System as any).instantiate.bind(System);
const cache = new Map<string, string>();
(System as any).instantiate = async function (url: string, parent: string) {
  const loader = this as ModuleSystem;
  console.debug(`instantiate`, url, parent);
  let next = url;
  if (url.startsWith('npm:')) {
    if (cache.has(url)) {
      return orig(cache.get(url), parent);
    }
    // @wener/reaction ->  @wener/reaction@1.2.11
    // @wener/reaction@latest ->  @wener/reaction@1.2.11
    // @wener/reaction@^1 ->  @wener/reaction@1.2.11
    // resolve the version by jsdelivr
    // cache the package.json for later use

    // https://cdn.jsdelivr.net/npm/@@wener/reaction@latest/package.json
    // https://ga.system.jspm.io/npm:@wener/reaction@1.2.11/package.json

    let repo = new URL(url).pathname;
    let {
      name,
      ver,
      path = '',
    } = repo.match(/^(?<name>@[^/]+\/[^/@]+|[^/@]+)(@(?<ver>[^/]+))?(?<path>\/.*)?$/)?.groups || {};
    if (!ver || /^\D/.test(ver)) {
      ver ||= 'latest';
      // just resolve the version, ignore path
      const p = `https://cdn.jsdelivr.net/npm/${name}@${ver}/package.json`;
      const mod = await loader.import(p);
      repo = `${name}@${mod.default.version}${path}`;
      // already load
      loader.set(`https://ga.system.jspm.io/npm:${repo}`, mod);
    }

    next = `https://ga.system.jspm.io/npm:${repo}`;

    // /lib -> /lib/package.json -> .module, .import, .main
    // /lib -> /lib/index.js
    // /lib -> /lib.js
    // /package.json -> .exports - TODO
    if (!/.(json|js)$/.test(next)) {
      try {
        const pkg = next + '/package.json';
        console.debug(`load package.json`, url, parent, pkg);
        const { default: p } = await loader.import(pkg);
        // can be improved to use the exports
        const to = p.module || p.import || p.main;
        if (to) {
          next = new URL(to, next + '/').toString();
        }
        console.debug(`resolve pkg`, next, p);
      } catch (e) {
        console.error(`load package.json failed`, e);
        // better check the exports to avoid try and fail
        try {
          await loader.import(next + '/index.js');
          next += '/index.js';
        } catch (e) {
          try {
            await loader.import(next + '.js');
            next += '.js';
          } catch (e) {
            throw new Error(`resolve ${url} failed`);
          }
        }
      }
    }
  }

  let result = orig(next, parent);
  if ('then' in result) {
    return result.then((v: any) => {
      if (url.startsWith('npm:')) {
        console.debug(`cache npm resolve:`, url, next);
        cache.set(url, next);
      }
      return v;
    });
  }
  return result;
};

but this plugin will not handle use-sync-external-store/shim/with-selector. for internal deps I suggest you manual load to prevent the multi version of core lib.

// lazy reexport
const BuiltinModules: Record<string, () => Promise<any>> = {
  // react
  react: () => import('react'),
  scheduler: () => import('scheduler'),
  'react/jsx-runtime': () => import('react/jsx-runtime'),
  'react-is': () => import('react-is'),
  'use-sync-external-store/shim/with-selector': () => import('use-sync-external-store/shim/with-selector' as any),
  'react-dom': () => import('react-dom'),
}
export function registerBuiltinModules(s: ModuleSystem) {
  Object.entries(BuiltinModules).map(([k, v]) => {
    // if mapped use s.resolve(k)
    s.register(k, [], (exports, module) => {
      return {
        execute: async () => {
          if (process.env.NODE_ENV !== 'production') {
            console.debug(`Loading builtin module ${k}`);
          }
          exports(await v());
        },
      };
    });
  });
}

image

the resolve process can be improved.