目录
1. CodeSandbox 是什么?
2. Sandpack Packager 是什么?
3. Sandpack Packager 基本流程?
4. Sandpack Packager 源码分析的前置知识
4.1. 官方仓库
4.2. 调试前注意事项
4.3. 【NPM库】:string-hash
4.4. 【NPM库】:meriyah
4.5. 【NPM库】:acorn-walk
4.6. 【NPM库】:pacote
4.7. 【NPM库】:npm-package-arg
4.8. 【NPM库】:browser-resolve
4.9. 【NPM库】:recursiveReaddir
4.10. 【NPM库】:recursive-readdir-sync
4.11. 【NPM库】:JSON5
4.12. 【NPM库】:fs
5. Sandpack Packager 源码分析
5.1. 关键数据结构
5.2. 总体流程
5.3. 依赖关系解析流程-getContents
5.4. 依赖关系解析流程-findDependencyDependencies
6. 演示
1. CodeSandbox 是什么?
CodeSandbox 是一个在线的代码编辑器,主要聚焦于创建 Web 应用项目。
2. Sandpack Packager 是什么?
CodeSandbox 大体上分3部分:Editor、Packager、Sandbox。
3. Sandpack Packager 基本流程?
{
// 模块内容
"contents": {
"/node_modules/react/index.js": {
"content": "'use strict';↵↵if ....", // 代码内容
"requires": [ // 依赖的其他模块
"./cjs/react.development.js",
],
},
"/node_modules/react-dom/index.js": {/*..*/},
"/node_modules/react/package.json": {/*...*/},
//...
},
// 模块具体安装版本号
"dependencies": [{name: "@babel/runtime", version: "7.3.1"}, {name: "csbbust", version: "1.0.0"},/*…*/],
// 模块别名, 比如将react作为preact-compat的别名
"dependencyAliases": {},
// 依赖的依赖, 即间接依赖信息. 这些信息可以从yarn.lock获取
"dependencyDependencies": {
"object-assign": {
"entries": ["object-assign"], // 模块入口
"parents": ["react", "prop-types", "scheduler", "react-dom"], // 父模块
"resolved": "4.1.1",
"semver": "^4.1.1",
}
//...
}
}
4. Sandpack Packager 源码分析的前置知识
4.1. 官方仓库
https://github.com/codesandbox/dependency-packager#sandpack-packager
4.2. 调试前注意事项
4.3. 【NPM库】:string-hash
A fast string hashing function for Node.JS. The hashing function returns a number between 0 and 4294967295 (inclusive).
const stringHash = require("string-hash");
console.log(stringHash("foo")); // prints "193420387"
4.4. 【NPM库】:meriyah
100% compliant, self-hosted javascript parser with high focus on both performance and stability. Stable and already used in production.
4.5. 【NPM库】:acorn-walk
An abstract syntax tree walker for the ESTree format.
const acorn = require("acorn")
const walk = require("acorn-walk")
walk.simple(acorn.parse("let x = 10"), {
Literal(node) {
console.log(`Found a literal: ${node.value}`)
}
})
4.6. 【NPM库】:pacote
Fetches package manifests and tarballs from the npm registry.
// index.js
const pacote = require('pacote');
pacote.manifest('echarts@latest').then(manifest => console.log(manifest));
4.7. 【NPM库】:npm-package-arg
Parses package name and specifier passed to commands like npm install or npm cache add, or as found in package.json dependency sections.
// index.js
const npa = require("npm-package-arg")
const parsed = npa('echarts@latest');
console.log(parsed);
4.8. 【NPM库】:resolve
implements the node require.resolve() algorithm such that you can require.resolve() on behalf of a file asynchronously and synchronously.
4.9. 【NPM库】:recursiveReaddir
Recursively list all files in a directory and its subdirectories. It does not list the directories themselves.
// index.js
var recursive = require("recursive-readdir");
recursive("d:/a", function (err, files) {
console.log(files);
});
4.10. 【NPM库】:recursive-readdir-sync
A simple Node module for synchronously listing all files in a directory, or in any subdirectories. It does not list directories themselves.
var recursiveReadSync = require("recursive-readdir-sync");
const files = recursiveReadSync("d:/a");
console.log(files);
4.11. 【NPM库】:JSON5
The JSON5 Data Interchange Format (JSON5) is a superset of JSON that aims to alleviate some of the limitations of JSON by expanding its syntax to include some productions from ECMAScript 5.1.
{
// comments
unquoted: 'and you can quote me on that',
singleQuotes: 'I can use "double quotes" here',
lineBreaks: "Look, Mom! \
No \\n's!",
hexadecimal: 0xdecaf,
leadingDecimalPoint: .8675309, andTrailing: 8675309.,
positiveSign: +1,
trailingComma: 'in objects', andIn: ['arrays',],
"backwardsCompatible": "with JSON",
}
4.12. 【NPM库】:fs
- D:/a
-- a.txt
-- b
---- c
------ c.txt
const fs = require('fs');
// 异步地读取目录的内容
fs.readdir('d:/a', (err, files) => {
if (err) throw err;
console.log(files);
});
const fs = require('fs');
const path = require("path");
const folder = "d:/a";
fs.readdir(folder, (err, files) => {
if (err) throw err;
files.forEach((file) => {
const absoluteFilePath = path.resolve(folder, file);
fs.stat(absoluteFilePath, function (err, stats) {
console.log(absoluteFilePath, file, stats.isDirectory());
});
});
});
5. Sandpack Packager 源码分析
5.1. 关键数据结构
interface IDependency {
name: string;
version: string;
}
export interface IPackage {
name: string;
main?: string;
browser?: string | { [path: string]: string | false };
unpkg?: string;
module?: string;
es2015?: string;
version: string;
dependencies?: {
[depName: string]: string;
};
peerDependencies?: {
[depName: string]: string;
};
[key: string]: any;
}
interface IFileContent {
path: string;
content: string;
}
interface IFileRequires {
isModule: boolean;
requires: string[];
}
export interface IFileData {
[path: string]: {
content: string;
isModule: boolean;
requires?: string[];
};
}
5.2. 总体流程
functions\packager\index.ts:
5.3. 依赖关系解析流程-getContents
function findRequires(
packageName: string,
rootPath: string,
packageInfos: { [dep: string]: IPackage },
): Promise<IFileData>
function buildRequireObject(
filePath: string,
packagePath: string,
existingContents: IFileData,
): IFileData
async function resolveRequiredFiles(
packagePath: string,
packageInfo: IPackage,
)
5.4. 依赖关系解析流程-findDependencyDependencies
interface IDependencyDependenciesInfo {
dependencyDependencies: {
[depName: string]: {
parents: string[];
semver: string;
resolved: string;
entries: string[];
};
};
dependencyAliases: {
[dep: string]: {
[dep: string]: string;
};
};
peerDependencies: { [depName: string]: string };
}
6. 演示
参考:
How we make npm packages work in the browser; announcing the new packager: https://hackernoon.com/how-we-make-npm-packages-work-in-the-browser-announcing-the-new-packager-6ce16aa4cee6 Creating a parallel, offline, extensible, browser based bundler for CodeSandbox: https://hackernoon.com/how-i-created-a-parallel-offline-extensible-browser-based-bundler-886db508cc31 Sandpack Packager: https://github.com/codesandbox/dependency-packager Webpack.DllPlugin: https://webpack.js.org/plugins/dll-plugin/ NPM Packager Service: http://webpack-dll-prod.herokuapp.com/ https://github.com/cerebral/webpack-packager CodeSandBox 作者 Ives van Hoorne 博客: https://medium.com/@compuives esnextb.in: https://github.com/voronianski/esnextbin 一些工具: https://github.com/meriyah/meriyah https://github.com/darkskyapp/string-hash https://github.com/acornjs/acorn/tree/master/acorn-walk https://github.com/npm/pacote https://github.com/npm/npm-package-arg https://github.com/browserify/resolve https://github.com/jergason/recursive-readdir https://github.com/json5/json5 ReasonML: https://reasonml.github.io/