Skip to content

Commit

Permalink
Merge pull request #475 from akashic-games/fix-key-to-register-in-scr…
Browse files Browse the repository at this point in the history
…iptcaches

Fix key to registered in scriptCahes
  • Loading branch information
ShinobuTakahashi authored Jan 12, 2024
2 parents 6aea6a3 + 274e2c8 commit 33a71dc
Show file tree
Hide file tree
Showing 5 changed files with 40 additions and 83 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# ChangeLog

## 3.16.4
不具合修正
* `require()` で末尾の "index" や "index.js" を省略する表記としない表記を混在させた時、スクリプトが複数回評価される問題を修正

## 3.16.3
* 3.16.2 の不具合回避のため 3.16.1 と同じ内容にリバート

Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@akashic/akashic-engine",
"version": "3.16.3",
"version": "3.16.4",
"description": "The core library of Akashic Engine",
"main": "index.js",
"dependencies": {
Expand Down
91 changes: 17 additions & 74 deletions src/ModuleManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,53 +68,21 @@ export class ModuleManager {
}
}

// 1. If X is a core module,
// (何もしない。コアモジュールには対応していない。ゲーム開発者は自分でコアモジュールへの依存を解決する必要がある)

if (/^\.\/|^\.\.\/|^\//.test(path)) {
// 2. If X begins with './' or '/' or '../'

if (currentModule) {
if (!currentModule._virtualDirname)
throw ExceptionFactory.createAssertionError("g._require: require from modules without virtualPath is not supported");
resolvedPath = PathUtil.resolvePath(currentModule._virtualDirname, path);
} else {
if (!/^\.\//.test(path)) throw ExceptionFactory.createAssertionError("g._require: entry point path must start with './'");
resolvedPath = path.substring(2);
}
if (!resolvedPath) {
resolvedPath = this._resolvePath(path, currentModule);
// 戻り値は先頭に "/" が付くので削除している。( moduleMainScripts を参照して返される値には先頭に "/" は付かない)
if (/^\//.test(resolvedPath)) resolvedPath = resolvedPath.slice(1);
}

if (this._scriptCaches.hasOwnProperty(resolvedPath)) {
return this._scriptCaches[resolvedPath]._cachedValue();
} else if (this._scriptCaches.hasOwnProperty(resolvedPath + ".js")) {
return this._scriptCaches[resolvedPath + ".js"]._cachedValue();
}
if (this._scriptCaches.hasOwnProperty(resolvedPath)) {
return this._scriptCaches[resolvedPath]._cachedValue();
}

// 2.a. LOAD_AS_FILE(Y + X)
if (!targetScriptAsset) targetScriptAsset = this._findAssetByPathAsFile(resolvedPath, liveAssetVirtualPathTable);
// 2.b. LOAD_AS_DIRECTORY(Y + X)
if (!targetScriptAsset) targetScriptAsset = this._findAssetByPathAsDirectory(resolvedPath, liveAssetVirtualPathTable);
// akashic-engine独自仕様: 対象の `path` が `moduleMainScripts` に指定されていたらそちらを参照する
if (moduleMainScripts[path]) {
targetScriptAsset = liveAssetVirtualPathTable[resolvedPath];
} else {
// 3. LOAD_NODE_MODULES(X, dirname(Y))
// `path` は node module の名前であると仮定して探す

// akashic-engine独自仕様: 対象の `path` が `moduleMainScripts` に指定されていたらそちらを参照する
if (moduleMainScripts[path]) {
resolvedPath = moduleMainScripts[path];
targetScriptAsset = liveAssetVirtualPathTable[resolvedPath];
}

if (!targetScriptAsset) {
const dirs = currentModule ? currentModule.paths : [];
dirs.push("node_modules");
for (let i = 0; i < dirs.length; ++i) {
const dir = dirs[i];
resolvedPath = PathUtil.resolvePath(dir, path);
targetScriptAsset = this._findAssetByPathAsFile(resolvedPath, liveAssetVirtualPathTable);
if (targetScriptAsset) break;
targetScriptAsset = this._findAssetByPathAsDirectory(resolvedPath, liveAssetVirtualPathTable);
if (targetScriptAsset) break;
}
}
targetScriptAsset = this._findAssetByPathAsFile(resolvedPath, liveAssetVirtualPathTable);
}

if (targetScriptAsset) {
Expand Down Expand Up @@ -175,7 +143,10 @@ export class ModuleManager {
}
resolvedPath = PathUtil.resolvePath(currentModule._virtualDirname, path);
} else {
throw ExceptionFactory.createAssertionError("g._require.resolve: couldn't resolve the moudle without currentModule");
if (!/^\.\//.test(path)) {
throw ExceptionFactory.createAssertionError("g._require.resolve: entry point path must start with './'");
}
resolvedPath = path.substring(2);
}

// 2.a. LOAD_AS_FILE(Y + X)
Expand Down Expand Up @@ -234,34 +205,6 @@ export class ModuleManager {
return undefined;
}

/**
* 与えられたパス文字列がディレクトリパスであると仮定して、対応するアセットを探す。
* 見つかった場合そのアセットを、そうでない場合 `undefined` を返す。
* 通常、ゲーム開発者がファイルパスを扱うことはなく、このメソッドを呼び出す必要はない。
* ディレクトリ内に package.json が存在する場合、package.json 自体もアセットとして
* `liveAssetPathTable` から参照可能でなければならないことに注意。
*
* @ignore
* @param resolvedPath パス文字列
* @param liveAssetPathTable パス文字列のプロパティに対応するアセットを格納したオブジェクト
*/
_findAssetByPathAsDirectory(resolvedPath: string, liveAssetPathTable: { [key: string]: OneOfAsset }): OneOfAsset | undefined {
let path: string;
path = resolvedPath + "/package.json";
const pkgJsonAsset = liveAssetPathTable[path];
// liveAssetPathTable[path] != null だけではpathと同名のprototypeプロパティがある場合trueになってしまうので hasOwnProperty() を利用
if (liveAssetPathTable.hasOwnProperty(path) && pkgJsonAsset.type === "text") {
const pkg = JSON.parse(pkgJsonAsset.data);
if (pkg && typeof pkg.main === "string") {
const asset = this._findAssetByPathAsFile(PathUtil.resolvePath(resolvedPath, pkg.main), liveAssetPathTable);
if (asset) return asset;
}
}
path = resolvedPath + "/index.js";
if (liveAssetPathTable.hasOwnProperty(path)) return liveAssetPathTable[path];
return undefined;
}

/**
* 与えられたパス文字列がファイルパスであると仮定して、対応するアセットの絶対パスを解決する。
* アセットが存在した場合はそのパスを、そうでない場合 `null` を返す。
Expand Down Expand Up @@ -297,7 +240,7 @@ export class ModuleManager {
if (pkg && typeof pkg.main === "string") {
const targetPath = this._resolveAbsolutePathAsFile(PathUtil.resolvePath(resolvedPath, pkg.main), liveAssetPathTable);
if (targetPath) {
return "/" + targetPath;
return targetPath;
}
}
}
Expand Down
22 changes: 16 additions & 6 deletions src/__tests__/ModuleSpec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -655,6 +655,16 @@ describe("test Module", () => {
expect(libraryA.thisModule.loaded).toBe(true);

expect(moduleUsesALibraryA).not.toBe(libraryA);

const keys = Object.keys(manager._scriptCaches);
expect(keys.includes("script/useA.js")).toBeTruthy();
expect(keys.includes("node_modules/moduleUsesA/index.js")).toBeTruthy();
expect(keys.includes("node_modules/moduleUsesA/node_modules/libraryA/index.js")).toBeTruthy();
expect(keys.includes("node_modules/moduleUsesA/node_modules/libraryA/lib/foo/foo.js")).toBeTruthy();
expect(keys.includes("node_modules/libraryA/index.js")).toBeTruthy();
// node_modules/libraryA が node_modules/libraryA/index.js として登録されていることを確認
expect(keys.includes("node_modules/libraryA")).toBeFalsy();

done();
});
game._startLoadingGlobalAssets();
Expand Down Expand Up @@ -906,30 +916,30 @@ describe("test Module", () => {
expect(manager._findAssetByPathAsFile("zoo/roo.js", liveAssetPathTable)).toBe(undefined);
});

it("_findAssetByPathDirectory", done => {
it("_resolveAbsolutePathAsDirectory", done => {
const game = new Game({ width: 320, height: 320, main: "", assets: {} });
const pkgJsonAsset = game.resourceFactory.createTextAsset("foopackagejson", "foo/package.json");
const liveAssetPathTable = {
"foo/root.js": game.resourceFactory.createScriptAsset("root", "/foo/root.js"),
"foo/package.json": pkgJsonAsset,
"foo/index.js": game.resourceFactory.createScriptAsset("fooindex", "/foo/index.js"),
"bar/index.js": game.resourceFactory.createScriptAsset("barindex", "/bar/index.js"),
"zoo/roo/notMain.js": game.resourceFactory.createScriptAsset("zooRooNotMain", "/zoo/roo/notMain.js")
};
const manager = game._moduleManager;
game.resourceFactory.scriptContents = {
"foo/package.json": '{ "main": "root.js" }'
};

pkgJsonAsset._load({
_onAssetError: e => {
throw e;
},
_onAssetLoad: () => {
try {
expect(manager._findAssetByPathAsDirectory("foo", liveAssetPathTable)).toBe(liveAssetPathTable["foo/root.js"]);
expect(manager._findAssetByPathAsDirectory("bar", liveAssetPathTable)).toBe(liveAssetPathTable["bar/index.js"]);
expect(manager._findAssetByPathAsDirectory("zoo/roo", liveAssetPathTable)).toBe(undefined);
expect(manager._findAssetByPathAsDirectory("tee", liveAssetPathTable)).toBe(undefined);
expect(manager._resolveAbsolutePathAsDirectory("foo", liveAssetPathTable)).toBe("/foo/root.js");
expect(manager._resolveAbsolutePathAsDirectory("bar", liveAssetPathTable)).toBe("/bar/index.js");
expect(manager._resolveAbsolutePathAsDirectory("zoo/roo", liveAssetPathTable)).toBeNull();
expect(manager._resolveAbsolutePathAsDirectory("hoge", liveAssetPathTable)).toBeNull();
} finally {
done();
}
Expand Down

0 comments on commit 33a71dc

Please sign in to comment.