Skip to content

Commit

Permalink
Rewrite dependency handling
Browse files Browse the repository at this point in the history
Signed-off-by: William So <polyipseity@gmail.com>
  • Loading branch information
polyipseity committed Aug 16, 2023
1 parent 2c80241 commit c944a54
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 68 deletions.
5 changes: 5 additions & 0 deletions .changeset/shy-ears-pay.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"obsidian-modules": minor
---

Rewrite dependency handling.
48 changes: 38 additions & 10 deletions sources/@types/obsidian-modules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,13 @@ declare module "obsidian-modules" {
*/
readonly import: (id: string, opts?: ImportOptions) => unknown

/**
* Invalidate the cache of a module or an alias.
*
* @param id module specifier
*/
readonly invalidate: (id: string) => void

/**
* Object for resolving module specifiers.
*/
Expand All @@ -47,12 +54,32 @@ declare module "obsidian-modules" {
/**
* Cache for loaded modules.
*/
readonly cache: WeakMap<Resolved["identity"], ModuleCache>
readonly cache: Record<string, ModuleCache>

/**
* Aliased modules.
*/
readonly aliased: Record<string, string>

/**
* Module aliases.
*/
readonly aliases: Record<string, Set<string>>

/**
* Context for loading modules.
*/
readonly context: Context

/**
* Module dependants.
*/
readonly dependants: Record<string, Set<string>>

/**
* Module dependencies.
*/
readonly dependencies: Record<string, Set<string>>
}

/**
Expand Down Expand Up @@ -138,11 +165,6 @@ declare module "obsidian-modules" {
*/
interface Resolved {

/**
* Identity of the resolved module.
*/
readonly identity: object

/**
* Module specifier of the resolved module.
*/
Expand All @@ -153,6 +175,13 @@ declare module "obsidian-modules" {
*/
readonly code: string

/**
* Whether to use cache.
*
* @default true
*/
readonly cache?: boolean

/**
* Exports of the resolved module.
*/
Expand All @@ -177,13 +206,12 @@ declare module "obsidian-modules" {
/**
* Identity of the parent module being loaded.
*/
parent?: Resolved["identity"]
parent?: string

/**
* Module dependencies.
* Invalidator.
*/
readonly dependencies: WeakMap<Resolved["identity"
], Set<Resolved["identity"]>>
invalidate: Require["invalidate"]
}
}
import type { } from "obsidian-modules"
Expand Down
69 changes: 49 additions & 20 deletions sources/require/require.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,18 @@ function createRequire(
resolved: Resolved | null,
): readonly [Resolved, ModuleCache] {
if (!resolved) { throw new Error(id) }
const { identity } = resolved
let cache = self1.cache.get(identity)
if (!cache) { self1.cache.set(identity, cache = {}) }
return [resolved, cache]
const { id: id2 } = resolved,
{ aliased, aliased: { [id]: oldID }, aliases, cache } = self1
aliased[id] = id2;
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
(aliases[id2] ??= new Set([id2])).add(id)
if (oldID !== void 0 && id2 !== oldID) {
aliases[oldID]?.delete(id)
self1.invalidate(id)
}
if (!(resolved.cache ?? true)) { cache[id2] = {} }
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition, no-return-assign
return [resolved, cache[id2] ??= {}]
}
function cache0<T>(
cache: ModuleCache,
Expand All @@ -88,27 +96,24 @@ function createRequire(
get,
})
}
function depends(resolved: Resolved, context: Context): void {
const { identity } = resolved,
{ parent, dependencies } = context
if (parent) {
let dep = dependencies.get(parent)
if (!dep) {
dep = new Set()
dependencies.set(parent, dep)
}
dep.add(identity)
}
function depends(self1: Require, id: string, context: Context): void {
const { dependencies, dependants } = self1,
{ parent } = context
if (parent === void 0) { return }
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
(dependencies[parent] ??= new Set()).add(id);
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
(dependants[id] ??= new Set()).add(parent)
}
function preload(
cleanup: Functions,
resolved: Resolved,
context: Context,
): void {
const { cwd, identity } = resolved,
const { cwd, id } = resolved,
{ cwds, parent } = context
cleanup.push(() => { assignExact(context, "parent", parent) })
context.parent = identity
context.parent = id
cwds.push(cwd)
cleanup.push(() => { cwds.pop() })
}
Expand All @@ -121,9 +126,9 @@ function createRequire(
cwds.push(cwd)
cleanup.push(() => cwds.pop())
}
depends(ret, id0, context)
const [rd, cache] = resolve0(ret, id0, resolve1.resolve(id0, context)),
{ code, id, value } = rd
depends(rd, context)
if ("commonJS" in cache) { return cache.commonJS }
if ("value" in rd) {
cache0(cache, "commonJS", constant(value))
Expand Down Expand Up @@ -177,11 +182,16 @@ function createRequire(
}
}, {
[REQUIRE_TAG]: true,
cache: new WeakMap(),
aliased: {},
aliases: {},
cache: {},
context: {
cwds: [],
dependencies: new WeakMap(),
invalidate(id: string) { ret.invalidate(id) },
},
dependants: {},
dependencies: {},
async import(id0: string, opts?: ImportOptions) {
const cleanup = new Functions({ async: false, settled: true })
try {
Expand All @@ -191,6 +201,7 @@ function createRequire(
cwds.push(cwd)
cleanup.push(() => { cwds.pop() })
}
depends(ret, id0, context)
const
[rd, cache] = resolve0(
ret,
Expand All @@ -201,7 +212,6 @@ function createRequire(
key = `esModule${opts?.commonJSInterop ?? true
? "WithCommonJS"
: ""}` as const
depends(rd, context)
if (key in cache) { return cache[key] }
if ("value" in rd) {
cache0(cache, key, constant(value))
Expand Down Expand Up @@ -373,6 +383,25 @@ function createRequire(
cleanup.call()
}
},
invalidate(id: string) {
const { aliased, aliases, cache, dependants, dependencies } = ret,
id2 = aliased[id] ?? id,
seen = new Set(),
ing = [...aliases[id2] ?? [id2]]
for (let cur = ing.shift(); cur !== void 0; cur = ing.shift()) {
if (seen.has(cur)) { continue }
seen.add(cur)
cache[cur] = {}
const dependencies2 = dependencies[cur]
for (const dep of dependencies2 ?? []) {
dependants[dep]?.delete(cur)
}
dependencies2?.clear()
for (const dep of dependants[cur] ?? []) {
ing.push(...aliases[dep] ?? [dep])
}
}
},
resolve,
})
return ret
Expand Down
Loading

0 comments on commit c944a54

Please sign in to comment.