想请教一下 Creator 3 对npm包的引入原理

好的,我再补充一下我的问题哈,用在node环境的那些,比如fs,path这些无法引入这一点我是理解的。我是全栈,两种环境都会接触,所以理解你所说的,我也不会用到这样的包,非常感谢你的解释!

只需要支持那些纯前端包/有前端版本的包就可以啦。axios之所以特殊是因为它同时可以用在前端和后端,用此它本身会尝试导入nodejs环境的包,就会导致报错。大概只需要让它知道自己是在浏览器环境而非node环境就可以了,大概?

对于最后大佬所说的,未来会继续对npm包引入这一点,允我表示对cocos团队的感谢,因为这一点很重要!

听说最近大佬最近在忙动画图,所以npm这里如果有什么需要测试或者讨论的,可以直接找我! :+1:

OK,欢迎继续反馈,我们正需要你这样的深度用户

1赞

好的呢,另外想一下现在负责npm这一块的同学是哪位呀?

老哥,可否帮个忙解决这个这npm的问题,我迫切需要这种json包 请问npm包该如何引入cocos中使用? - Creator 3.x - Cocos中文社区
有没有临时的解决方案提供一下?

npm 的问题反馈给我好了

1赞

@_PP 您好,正好有另一个npm相关的问题想问一下:

我有一个自己构建的 npm 包,因为是放在私有源的,就不提供复现代码了,我尽量描述一下情况:

假设有一个叫api包,其中是这样的:

// index.ts
export const apiBase = ...

我使用 npm 在cocos 项目中安装了这个包,并且将tsconfig.json中的allowSyntheticDefaultImports设为true。接下来做如下测试:

import api from "api";
console.log({ api });

控制台正常,接下来将代码换成:

import * as api from "api";
console.log({ api });

将发现 default 重新出现了。进一步的,如果使用 deconstruct 语法进行引入:

import { apiBase } from "api";
console.log({ apiBase });

会导致循环引用并无法获得导出的对象:

进一步发现,这样引入同样是引入到了 default 对象中,即:

import { default as _default } from "api";
console.log({ _default });

同样的问题出现在另一篇帖子里:
请问npm包该如何引入cocos中使用? - Creator 3.x - Cocos中文社区


我也碰到这个问题。

大家可以一起 push 官方 :smirk: 如果有类似的问题或者解决办法或者思路可以跟帖~

3.5.2也出现,非常感谢老哥的鼎力支持,希望官方看下我们俩的2个帖子,找到修复方式

我猜测你这里 npm 包源码是 esm 格式的 ts 文件,是否还会通过 tsc 编译成 CommonJS 的 js 文件呢?如果是这样的话,应该会编译成

// index.js
module.exports.apiBase = ...

而编辑器的 build tool 只会处理 npm 包实际导出的 js 文件,即 index.js
这个 index.js 会被编辑器的 build tool 做多一次包装,包装成类似这样的模块

export const __cjsMetaURL = 'pack:///chunks/...';
export const default = {
    apiBase: ...,
};  // 注意:这个 default 对象的所有属性是运行时动态赋值的,因为 CommonJS 并不能被很好地支持静态解析
// 所以目前也没有办法解析生成  `export const apiBase = ...` 这样的导出语句。

所以后面的一系列输出应该就可以理解了,

import api from "api";  // 其实就是 `import { default as api } from 'api'`, api 就是 default 对象

import * as api from "api";   // 输出就是 index.js 导出的所有对象,包括 __cjsMetaURL  和 default

import { apiBase } from "api"; // 就有问题了,因为 index.js 没有导出 apiBase 对象, 就报错了
// 这里可能报错信息有一些误导性,应该不是循环引用导致的,这个后续我们优化一下

import { default as _default } from "api";  // 同上 `import api from "api";`
1赞

所以可以被解决吗?因为npm上其他包也出现了类似的问题,我知道这样说有点委屈你们,但是无论原理上是什么样子,无法正常引入就可以算是cocos的bug :joy: 因为一个npm包没办法用正常的方式使用了。

我的包是用比较规范的方式构建的,cjs、esm格式都有。用的是字节的modernjs框架直接生成的包。如果有任何问题,需要我的都可以问我,我会尽量提供细节。

1赞

问一下目前 npm 包是否配置了条件导出呢,不太确定是不是我们的 build tool 对条件导出的解析有问题,如果解析到 cjs 的入口,可能就出错了

可以贴一下 package.json 下 exports 字段的相关信息吗

这样,我之前提到的另一个帖子中的 typedjson这个包也是同样的问题,如果方便的话可以测试一下。我一会儿也再找一些类似的例子看看。 package的信息我稍等贴出来

package.json相关内容:

  "jsnext:source": "./src/index.ts",
  "types": "./dist/types/index.d.ts",
  "main": "./dist/js/node/index.js",
  "module": "./dist/js/treeshaking/index.js",
  "jsnext:modern": "./dist/js/modern/index.js",

构建结果:

image

modern 里面:

export const apiBase = ...

node 里面:

exports.apiBase = apiBase;

treeshaking 里面:

export var apiBase = ...

了解了,我这边也做了一些测试,node 上确实是能够支持 ESM 模块以具名导入的方式导入 CJS 模块的,但是支持也是相对有限,

例如下边例子中的变量 d

// main.mjs
import { a, b, c, d } from './test.cjs';
console.log(a, b, c, d)

setTimeout(() => {
    console.log(a, b, c, d)  // 这里的 d 依旧是 undefined,是不支持 live binding 的
}, 2000)

// test.cjs
module.exports.a = 'a';
module.exports['b'] = 'b';
Object.defineProperty(module.exports, 'c', {
    value: 'c'
});
setTimeout(() => {
    console.log('dynamic assign d');
    module.exports['d'] = 'd';
}, 1000)

下边这种情况也不支持


// main.mjs
import { test } from './test.cjs';
console.log(test)

// test.cjs
module.exports['te' + 'st'] = 'test';

我们可以排期把前边 a b c 的情况支持掉

1赞

好的 :+1: 非常感谢

以及请问cocos对多种导入模式的优先级是怎样的?

应该是以 conditional exports 为最高优先级,其次是 main 作为 legacy 方案

conditional exports 不支持 node 导出,因为 node 环境并不能实现跨平台

1赞

你的还没能使用吗?心痛1999

没有,我这边也可以用同样的方法解决一下,但是并不是完美方案。

哥们呢还是别纠结了,其他bug还一堆呢,官方很忙,不建议在这方面浪费精力