@pathmx/core 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +172 -0
- package/README.md +111 -0
- package/actions/actions.ts +307 -0
- package/actions/draft.ts +105 -0
- package/actions/transaction.ts +164 -0
- package/assets.ts +149 -0
- package/authority/access.ts +820 -0
- package/authority/actors.ts +271 -0
- package/authority/agents.ts +95 -0
- package/authority/credentials.ts +144 -0
- package/authority/model.ts +71 -0
- package/authority/sharing.ts +89 -0
- package/browser-bundle.ts +61 -0
- package/canonical.ts +41 -0
- package/cli/dev.ts +134 -0
- package/cli/export.ts +74 -0
- package/cli/index.ts +52 -0
- package/cli/lint.ts +164 -0
- package/cli/mv.ts +30 -0
- package/cli/perms.ts +182 -0
- package/cli/plugin-packages.ts +159 -0
- package/cli/plugins.ts +95 -0
- package/cli/query.ts +225 -0
- package/cli/readme.md +95 -0
- package/cli/release.ts +75 -0
- package/cli/rm.ts +22 -0
- package/cli/serve.ts +100 -0
- package/cli/tree.ts +131 -0
- package/cli/update.ts +78 -0
- package/cli/utils.ts +53 -0
- package/core.ts +96 -0
- package/database.ts +189 -0
- package/dependencies.ts +60 -0
- package/dist/pathmx.js +1224 -0
- package/document.ts +194 -0
- package/environment.ts +110 -0
- package/environments/bun-plugins.ts +68 -0
- package/environments/bun-publish.ts +194 -0
- package/environments/bun.ts +223 -0
- package/globals.d.ts +14 -0
- package/graph.ts +153 -0
- package/host/compiler.ts +314 -0
- package/host/create.ts +66 -0
- package/host/engine.ts +630 -0
- package/host/plugin-host.ts +487 -0
- package/host/render.ts +102 -0
- package/host/response.ts +20 -0
- package/host/shell.html +15 -0
- package/host/source-sync.ts +254 -0
- package/html-attributes.ts +15 -0
- package/html.ts +48 -0
- package/icons/LICENSE +43 -0
- package/icons/lucide.ts +54 -0
- package/index.ts +75 -0
- package/invalidation.ts +85 -0
- package/log.ts +84 -0
- package/ops/href.ts +87 -0
- package/ops/index.ts +9 -0
- package/ops/move-source.ts +114 -0
- package/ops/plan.ts +47 -0
- package/ops/remove-source.ts +70 -0
- package/package.json +46 -0
- package/path.ts +75 -0
- package/pattern.ts +314 -0
- package/perf.ts +88 -0
- package/plugins/actor-path.ts +43 -0
- package/plugins/actor-profile.ts +55 -0
- package/plugins/agent-browsing.ts +183 -0
- package/plugins/app-component.ts +23 -0
- package/plugins/block-layout/index.ts +78 -0
- package/plugins/block-layout/theme.css +90 -0
- package/plugins/callout/index.ts +236 -0
- package/plugins/callout/theme.css +138 -0
- package/plugins/canonical-links.ts +144 -0
- package/plugins/code-highlight/client.ts +89 -0
- package/plugins/code-highlight/index.ts +147 -0
- package/plugins/code-highlight/theme.css +89 -0
- package/plugins/context.ts +131 -0
- package/plugins/datatype/Datatype[wdth,wght].woff2 +0 -0
- package/plugins/datatype/OFL.txt +96 -0
- package/plugins/datatype/index.ts +287 -0
- package/plugins/definition-list.ts +236 -0
- package/plugins/directive-definitions.ts +13 -0
- package/plugins/embedding/client.ts +88 -0
- package/plugins/embedding/index.ts +43 -0
- package/plugins/image/index.ts +68 -0
- package/plugins/image/theme.css +43 -0
- package/plugins/include.ts +119 -0
- package/plugins/index.ts +91 -0
- package/plugins/input/authoring.ts +236 -0
- package/plugins/input/config.ts +85 -0
- package/plugins/input/index.ts +359 -0
- package/plugins/input/model.ts +173 -0
- package/plugins/input/state.ts +272 -0
- package/plugins/layout.ts +313 -0
- package/plugins/literate/client.ts +208 -0
- package/plugins/literate/index.ts +742 -0
- package/plugins/math/index.ts +140 -0
- package/plugins/meta.ts +42 -0
- package/plugins/navigation/index.ts +158 -0
- package/plugins/navigation/model.ts +364 -0
- package/plugins/preset.ts +65 -0
- package/plugins/query/index.ts +322 -0
- package/plugins/rss/index.ts +46 -0
- package/plugins/runtime/actions.ts +62 -0
- package/plugins/runtime/client.ts +8 -0
- package/plugins/runtime/document.ts +506 -0
- package/plugins/runtime/error.ts +154 -0
- package/plugins/runtime/heading-links.ts +31 -0
- package/plugins/runtime/index.ts +104 -0
- package/plugins/runtime/live.ts +51 -0
- package/plugins/runtime/navigation.ts +113 -0
- package/plugins/runtime/pending.ts +53 -0
- package/plugins/runtime/publication.ts +19 -0
- package/plugins/runtime/start.ts +177 -0
- package/plugins/runtime/static-client.ts +6 -0
- package/plugins/runtime/static-live.ts +188 -0
- package/plugins/runtime/view-transition.ts +72 -0
- package/plugins/runtime/viewport.ts +110 -0
- package/plugins/schema.ts +24 -0
- package/plugins/scripts.ts +140 -0
- package/plugins/source-preview/client.ts +256 -0
- package/plugins/source-preview/index.ts +132 -0
- package/plugins/source-preview/model.ts +11 -0
- package/plugins/source-preview/preview.css +107 -0
- package/plugins/styles.ts +145 -0
- package/plugins/theme/components.ts +10 -0
- package/plugins/theme/foundation.css +358 -0
- package/plugins/theme/index.ts +333 -0
- package/plugins/theme/prose.css +284 -0
- package/plugins/theme/scheme.js +63 -0
- package/plugins/theme/toggle.css +44 -0
- package/plugins/theme/view-transition.css +68 -0
- package/plugins/toc/client.ts +146 -0
- package/plugins/toc/index.ts +232 -0
- package/plugins/toc/theme.css +172 -0
- package/plugins/types.ts +538 -0
- package/problem.ts +41 -0
- package/project.ts +6 -0
- package/publish/build.ts +402 -0
- package/publish/concurrent.ts +32 -0
- package/publish/identity.ts +63 -0
- package/publish/index.ts +11 -0
- package/publish/location.ts +167 -0
- package/publish/model.ts +58 -0
- package/publish/plan.ts +300 -0
- package/publish/validate.ts +150 -0
- package/query/candidate-index.ts +101 -0
- package/query/dependency-index.ts +99 -0
- package/query/fields.ts +216 -0
- package/query/invalidation.ts +48 -0
- package/query/kernel.ts +776 -0
- package/query/model.ts +79 -0
- package/repository.ts +374 -0
- package/runtime-profile.ts +34 -0
- package/schema/model.ts +92 -0
- package/schema/registry.ts +642 -0
- package/schema/standard.ts +226 -0
- package/server/context.ts +94 -0
- package/server/css-imports.ts +66 -0
- package/server/http.ts +333 -0
- package/server/page.ts +21 -0
- package/server/protocols.ts +12 -0
- package/server/representations.ts +66 -0
- package/server/router.ts +131 -0
- package/server/routes.ts +202 -0
- package/server/serve.ts +5 -0
- package/server/watch.ts +120 -0
- package/source/blocks.ts +71 -0
- package/source/format.ts +164 -0
- package/source/index.ts +67 -0
- package/source/interpolate.ts +63 -0
- package/source/markdown/blocks.ts +46 -0
- package/source/markdown/comments.ts +32 -0
- package/source/markdown/fences.ts +67 -0
- package/source/markdown/headings.ts +88 -0
- package/source/markdown/index.ts +34 -0
- package/source/markdown/inline-code.ts +6 -0
- package/source/markdown/links.ts +326 -0
- package/source/markdown/lists.ts +223 -0
- package/source/markdown/prose.ts +74 -0
- package/source/markdown/scan.ts +79 -0
- package/source/markdown/text.ts +82 -0
- package/source/meta.ts +87 -0
- package/source/source.ts +121 -0
- package/source/topmatter.ts +57 -0
- package/source/utils.ts +21 -0
- package/source-diagnostic.ts +63 -0
- package/text-file.ts +26 -0
- package/util.ts +35 -0
- package/view/components.ts +499 -0
- package/view/model.ts +91 -0
- package/view/projection.ts +162 -0
- package/view/session.ts +146 -0
- package/view/syntax.ts +372 -0
package/ops/href.ts
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { posixPath } from "../path.ts"
|
|
2
|
+
import { publicPath } from "../server/router.ts"
|
|
3
|
+
import type { Source } from "../source/index.ts"
|
|
4
|
+
|
|
5
|
+
export function splitHref(href: string) {
|
|
6
|
+
const hash = href.indexOf("#")
|
|
7
|
+
const withoutHash = hash === -1 ? href : href.slice(0, hash)
|
|
8
|
+
const query = withoutHash.indexOf("?")
|
|
9
|
+
return {
|
|
10
|
+
pathname: query === -1 ? withoutHash : withoutHash.slice(0, query),
|
|
11
|
+
query: query === -1 ? "" : withoutHash.slice(query),
|
|
12
|
+
fragment: hash === -1 ? "" : href.slice(hash),
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function joinHref(pathname: string, query: string, fragment: string) {
|
|
17
|
+
return `${pathname}${query}${fragment}`
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function isRelativeHref(pathname: string) {
|
|
21
|
+
return (
|
|
22
|
+
Boolean(pathname) && !pathname.startsWith("/") && !pathname.startsWith("~")
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function authoredId(source: Source) {
|
|
27
|
+
const id = source.data.id
|
|
28
|
+
return typeof id === "string" && id.trim() ? id.trim() : undefined
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function isAuthoredIdHref(pathname: string, source: Source) {
|
|
32
|
+
const id = authoredId(source)
|
|
33
|
+
return Boolean(id && (pathname === id || pathname === `/${id}`))
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function relativeHref(
|
|
37
|
+
fromPath: string,
|
|
38
|
+
targetPath: string,
|
|
39
|
+
originalHref: string,
|
|
40
|
+
) {
|
|
41
|
+
const fromDir = posixPath.dirname(fromPath)
|
|
42
|
+
let rel = posixPath.relative(fromDir, targetPath)
|
|
43
|
+
if (!rel) rel = posixPath.basename(targetPath)
|
|
44
|
+
if (
|
|
45
|
+
(originalHref.startsWith("./") &&
|
|
46
|
+
!rel.startsWith("../") &&
|
|
47
|
+
!rel.startsWith("/")) ||
|
|
48
|
+
(!rel.startsWith(".") && !rel.startsWith("/"))
|
|
49
|
+
) {
|
|
50
|
+
rel = `./${rel}`
|
|
51
|
+
}
|
|
52
|
+
return rel
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function rebaseRelativeHref(
|
|
56
|
+
href: string,
|
|
57
|
+
oldDir: string,
|
|
58
|
+
newDir: string,
|
|
59
|
+
) {
|
|
60
|
+
const { pathname, query, fragment } = splitHref(href)
|
|
61
|
+
if (!isRelativeHref(pathname)) return href
|
|
62
|
+
const oldTarget = posixPath.normalize(posixPath.join(oldDir, pathname))
|
|
63
|
+
return joinHref(
|
|
64
|
+
relativeHref(posixPath.join(newDir, "_"), oldTarget, href),
|
|
65
|
+
query,
|
|
66
|
+
fragment,
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function retargetHref(
|
|
71
|
+
href: string,
|
|
72
|
+
fromPath: string,
|
|
73
|
+
oldSource: Source,
|
|
74
|
+
newSource: Source,
|
|
75
|
+
): string | undefined {
|
|
76
|
+
const { pathname, query, fragment } = splitHref(href)
|
|
77
|
+
if (!pathname) return
|
|
78
|
+
if (isAuthoredIdHref(pathname, oldSource)) return
|
|
79
|
+
|
|
80
|
+
const explicitMd = pathname.endsWith(".md")
|
|
81
|
+
const target = explicitMd ? newSource.path : publicPath(newSource)
|
|
82
|
+
const absTarget = target.startsWith("/") ? target : `/${target}`
|
|
83
|
+
const next = isRelativeHref(pathname)
|
|
84
|
+
? relativeHref(fromPath, absTarget, href)
|
|
85
|
+
: target
|
|
86
|
+
return joinHref(next, query, fragment)
|
|
87
|
+
}
|
package/ops/index.ts
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import type { Engine } from "../host/engine.ts"
|
|
2
|
+
import { posixPath } from "../path.ts"
|
|
3
|
+
import { isIndexSource, publicPath } from "../server/router.ts"
|
|
4
|
+
import { createSource, rewriteLinks, type Source } from "../source/index.ts"
|
|
5
|
+
import {
|
|
6
|
+
authoredId,
|
|
7
|
+
isRelativeHref,
|
|
8
|
+
rebaseRelativeHref,
|
|
9
|
+
retargetHref,
|
|
10
|
+
splitHref,
|
|
11
|
+
} from "./href.ts"
|
|
12
|
+
import { emptyPlan, SourceOpError, type SourceOpPlan } from "./plan.ts"
|
|
13
|
+
|
|
14
|
+
function resolveExisting(app: Engine, input: string): Source {
|
|
15
|
+
const graphPath = app.repo.graphPath(input)
|
|
16
|
+
const source =
|
|
17
|
+
app.repo.atPath(graphPath) ??
|
|
18
|
+
app.repo.get(input) ??
|
|
19
|
+
app.repo.get(app.repo.pathToId(graphPath))
|
|
20
|
+
if (!source) throw new SourceOpError(`Source not found: ${input}`)
|
|
21
|
+
if (!source.path.endsWith(".md")) {
|
|
22
|
+
throw new SourceOpError(`Only markdown sources can be moved: ${input}`)
|
|
23
|
+
}
|
|
24
|
+
return source
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function resolveDest(app: Engine, input: string) {
|
|
28
|
+
const path = app.repo.graphPath(input)
|
|
29
|
+
if (!path.endsWith(".md")) {
|
|
30
|
+
throw new SourceOpError(`Destination must be a markdown path: ${input}`)
|
|
31
|
+
}
|
|
32
|
+
const storagePath = app.repo.storagePath(path)
|
|
33
|
+
if (!storagePath)
|
|
34
|
+
throw new SourceOpError(`Destination escapes the source root: ${input}`)
|
|
35
|
+
if (
|
|
36
|
+
app.repo.atPath(path) ||
|
|
37
|
+
(await app.environment.storage.exists(storagePath))
|
|
38
|
+
) {
|
|
39
|
+
throw new SourceOpError(`Destination already exists: ${path}`)
|
|
40
|
+
}
|
|
41
|
+
return path
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function referrerPath(from: string) {
|
|
45
|
+
const hash = from.indexOf("#")
|
|
46
|
+
return hash === -1 ? from : from.slice(0, hash)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export async function moveSource(
|
|
50
|
+
app: Engine,
|
|
51
|
+
from: string,
|
|
52
|
+
to: string,
|
|
53
|
+
): Promise<SourceOpPlan> {
|
|
54
|
+
const source = resolveExisting(app, from)
|
|
55
|
+
const dest = await resolveDest(app, to)
|
|
56
|
+
if (posixPath.normalize(source.path) === posixPath.normalize(dest)) {
|
|
57
|
+
throw new SourceOpError(`Source and destination are the same path: ${dest}`)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const preview = createSource(dest, source.content, app.environment.yaml)
|
|
61
|
+
const oldDir = posixPath.dirname(source.path)
|
|
62
|
+
const newDir = posixPath.dirname(dest)
|
|
63
|
+
const selfHrefs = new Set(
|
|
64
|
+
app.graph
|
|
65
|
+
.linksFrom(source.path)
|
|
66
|
+
.filter((edge) => edge.to === source.path)
|
|
67
|
+
.map((edge) => edge.href),
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
const movedContent = rewriteLinks(source.content, (href) => {
|
|
71
|
+
if (selfHrefs.has(href)) return retargetHref(href, dest, source, preview)
|
|
72
|
+
const { pathname } = splitHref(href)
|
|
73
|
+
if (isRelativeHref(pathname))
|
|
74
|
+
return rebaseRelativeHref(href, oldDir, newDir)
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
const plan = emptyPlan()
|
|
78
|
+
plan.writes.push({ path: dest, content: movedContent })
|
|
79
|
+
plan.deletes.push(source.path)
|
|
80
|
+
|
|
81
|
+
const inbound = app.graph.linksTo(source.path)
|
|
82
|
+
const byFile = new Map<string, Set<string>>()
|
|
83
|
+
for (const edge of inbound) {
|
|
84
|
+
const file = referrerPath(edge.from)
|
|
85
|
+
if (file === source.path) continue
|
|
86
|
+
const hrefs = byFile.get(file) ?? new Set()
|
|
87
|
+
hrefs.add(edge.href)
|
|
88
|
+
byFile.set(file, hrefs)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
for (const [file, hrefs] of byFile) {
|
|
92
|
+
const referrer = app.repo.atPath(file)
|
|
93
|
+
if (!referrer) continue
|
|
94
|
+
const next = rewriteLinks(referrer.content, (href) => {
|
|
95
|
+
if (!hrefs.has(href)) return
|
|
96
|
+
return retargetHref(href, file, source, preview)
|
|
97
|
+
})
|
|
98
|
+
if (next !== referrer.content)
|
|
99
|
+
plan.writes.push({ path: file, content: next })
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (isIndexSource(source)) {
|
|
103
|
+
plan.warnings.push(
|
|
104
|
+
`Moving index source ${source.path} changes the directory route ${publicPath(source)}`,
|
|
105
|
+
)
|
|
106
|
+
}
|
|
107
|
+
if (!authoredId(source) && source.id !== preview.id) {
|
|
108
|
+
plan.warnings.push(
|
|
109
|
+
`Moving ${source.path} changes its public URL from ${source.id} to ${preview.id}`,
|
|
110
|
+
)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return plan
|
|
114
|
+
}
|
package/ops/plan.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { Engine } from "../host/engine.ts"
|
|
2
|
+
import type { SourceChange } from "../repository.ts"
|
|
3
|
+
|
|
4
|
+
export type SourceOpWrite = {
|
|
5
|
+
path: string
|
|
6
|
+
content: string
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export type SourceOpPlan = {
|
|
10
|
+
writes: SourceOpWrite[]
|
|
11
|
+
deletes: string[]
|
|
12
|
+
warnings: string[]
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export class SourceOpError extends Error {
|
|
16
|
+
constructor(message: string) {
|
|
17
|
+
super(message)
|
|
18
|
+
this.name = "SourceOpError"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function emptyPlan(): SourceOpPlan {
|
|
23
|
+
return { writes: [], deletes: [], warnings: [] }
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export async function applySourceOpPlan(app: Engine, plan: SourceOpPlan) {
|
|
27
|
+
const changes: SourceChange[] = []
|
|
28
|
+
for (const path of plan.deletes) {
|
|
29
|
+
const graphPath = app.repo.graphPath(path)
|
|
30
|
+
if (!app.repo.storagePath(graphPath)) {
|
|
31
|
+
throw new SourceOpError(`Path escapes repository: ${path}`)
|
|
32
|
+
}
|
|
33
|
+
changes.push({ type: "delete", path: graphPath })
|
|
34
|
+
}
|
|
35
|
+
for (const write of plan.writes) {
|
|
36
|
+
const path = app.repo.graphPath(write.path)
|
|
37
|
+
if (!app.repo.storagePath(path)) {
|
|
38
|
+
throw new SourceOpError(`Path escapes repository: ${write.path}`)
|
|
39
|
+
}
|
|
40
|
+
changes.push({
|
|
41
|
+
type: app.repo.atPath(path) ? "replace" : "create",
|
|
42
|
+
path,
|
|
43
|
+
content: write.content,
|
|
44
|
+
})
|
|
45
|
+
}
|
|
46
|
+
if (changes.length) await app.commit({ changes })
|
|
47
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { Engine } from "../host/engine.ts"
|
|
2
|
+
import { isIndexSource, publicPath } from "../server/router.ts"
|
|
3
|
+
import { unlinkLinks, type Source } from "../source/index.ts"
|
|
4
|
+
import { emptyPlan, SourceOpError, type SourceOpPlan } from "./plan.ts"
|
|
5
|
+
|
|
6
|
+
function resolveExisting(app: Engine, input: string): Source {
|
|
7
|
+
const graphPath = app.repo.graphPath(input)
|
|
8
|
+
const source =
|
|
9
|
+
app.repo.atPath(graphPath) ??
|
|
10
|
+
app.repo.get(input) ??
|
|
11
|
+
app.repo.get(app.repo.pathToId(graphPath))
|
|
12
|
+
if (!source) throw new SourceOpError(`Source not found: ${input}`)
|
|
13
|
+
if (!source.path.endsWith(".md")) {
|
|
14
|
+
throw new SourceOpError(`Only markdown sources can be removed: ${input}`)
|
|
15
|
+
}
|
|
16
|
+
return source
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function referrerPath(from: string) {
|
|
20
|
+
const hash = from.indexOf("#")
|
|
21
|
+
return hash === -1 ? from : from.slice(0, hash)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export async function removeSource(
|
|
25
|
+
app: Engine,
|
|
26
|
+
path: string,
|
|
27
|
+
): Promise<SourceOpPlan> {
|
|
28
|
+
const source = resolveExisting(app, path)
|
|
29
|
+
const inbound = app.graph.linksTo(source.path)
|
|
30
|
+
const byFile = new Map<string, Set<string>>()
|
|
31
|
+
let blockTargets = 0
|
|
32
|
+
|
|
33
|
+
for (const edge of inbound) {
|
|
34
|
+
const file = referrerPath(edge.from)
|
|
35
|
+
if (file === source.path) continue
|
|
36
|
+
const hrefs = byFile.get(file) ?? new Set()
|
|
37
|
+
hrefs.add(edge.href)
|
|
38
|
+
byFile.set(file, hrefs)
|
|
39
|
+
if (
|
|
40
|
+
edge.fragment &&
|
|
41
|
+
source.blocks.some((block) => block.id === edge.fragment)
|
|
42
|
+
) {
|
|
43
|
+
blockTargets++
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const plan = emptyPlan()
|
|
48
|
+
plan.deletes.push(source.path)
|
|
49
|
+
|
|
50
|
+
for (const [file, hrefs] of byFile) {
|
|
51
|
+
const referrer = app.repo.atPath(file)
|
|
52
|
+
if (!referrer) continue
|
|
53
|
+
const next = unlinkLinks(referrer.content, (href) => hrefs.has(href))
|
|
54
|
+
if (next !== referrer.content)
|
|
55
|
+
plan.writes.push({ path: file, content: next })
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (isIndexSource(source)) {
|
|
59
|
+
plan.warnings.push(
|
|
60
|
+
`Removing index source ${source.path} will 404 the directory route ${publicPath(source)}`,
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
if (blockTargets) {
|
|
64
|
+
plan.warnings.push(
|
|
65
|
+
`Removing ${source.path} unlinks ${blockTargets} block target${blockTargets === 1 ? "" : "s"}`,
|
|
66
|
+
)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return plan
|
|
70
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pathmx/core",
|
|
3
|
+
"description": "A minimal, plugin-first framework for Paths and learning applications.",
|
|
4
|
+
"version": "0.5.0",
|
|
5
|
+
"license": "SEE LICENSE IN LICENSE.md",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/pathmx/pathmx-beta.git",
|
|
9
|
+
"directory": "pathmx/core"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/pathmx/pathmx-beta#readme",
|
|
12
|
+
"bugs": "https://github.com/pathmx/pathmx-beta/issues",
|
|
13
|
+
"publishConfig": {
|
|
14
|
+
"access": "public",
|
|
15
|
+
"registry": "https://registry.npmjs.org/",
|
|
16
|
+
"tag": "beta"
|
|
17
|
+
},
|
|
18
|
+
"type": "module",
|
|
19
|
+
"types": "./core.ts",
|
|
20
|
+
"module": "./core.ts",
|
|
21
|
+
"bin": {
|
|
22
|
+
"pathmx": "dist/pathmx.js",
|
|
23
|
+
"pmx": "dist/pathmx.js"
|
|
24
|
+
},
|
|
25
|
+
"exports": {
|
|
26
|
+
".": "./core.ts",
|
|
27
|
+
"./bun": "./index.ts",
|
|
28
|
+
"./publish": "./publish/index.ts",
|
|
29
|
+
"./plugins": "./plugins/index.ts"
|
|
30
|
+
},
|
|
31
|
+
"engines": {
|
|
32
|
+
"bun": ">=1.4.0"
|
|
33
|
+
},
|
|
34
|
+
"pathmxRelease": {
|
|
35
|
+
"channel": "beta",
|
|
36
|
+
"testedBun": "1.4.0"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@parcel/watcher": "^2.6.0",
|
|
40
|
+
"commander": "^15.0.0",
|
|
41
|
+
"highlight.js": "^11.12.0",
|
|
42
|
+
"morphdom": "^2.7.8",
|
|
43
|
+
"picocolors": "^1.1.1",
|
|
44
|
+
"temml": "^0.13.4"
|
|
45
|
+
}
|
|
46
|
+
}
|
package/path.ts
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
function parts(value: string) {
|
|
2
|
+
return value.split("/").filter(Boolean)
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
function normalize(value: string) {
|
|
6
|
+
if (!value) return "."
|
|
7
|
+
const absolute = value.startsWith("/")
|
|
8
|
+
const stack: string[] = []
|
|
9
|
+
for (const part of value.split("/")) {
|
|
10
|
+
if (!part || part === ".") continue
|
|
11
|
+
if (part === "..") {
|
|
12
|
+
if (stack.length && stack.at(-1) !== "..") stack.pop()
|
|
13
|
+
else if (!absolute) stack.push(part)
|
|
14
|
+
} else {
|
|
15
|
+
stack.push(part)
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
const normalized = `${absolute ? "/" : ""}${stack.join("/")}`
|
|
19
|
+
return normalized || (absolute ? "/" : ".")
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function join(...values: string[]) {
|
|
23
|
+
return normalize(values.filter(Boolean).join("/"))
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function dirname(value: string) {
|
|
27
|
+
const normalized = normalize(value)
|
|
28
|
+
if (normalized === "/" || normalized === ".") return normalized
|
|
29
|
+
const index = normalized.lastIndexOf("/")
|
|
30
|
+
if (index === -1) return "."
|
|
31
|
+
return index === 0 ? "/" : normalized.slice(0, index)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function basename(value: string) {
|
|
35
|
+
const normalized = normalize(value)
|
|
36
|
+
if (normalized === "/" || normalized === ".") return normalized
|
|
37
|
+
return normalized.slice(normalized.lastIndexOf("/") + 1)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function relative(from: string, to: string) {
|
|
41
|
+
const source = parts(normalize(from))
|
|
42
|
+
const target = parts(normalize(to))
|
|
43
|
+
let shared = 0
|
|
44
|
+
while (
|
|
45
|
+
shared < source.length &&
|
|
46
|
+
shared < target.length &&
|
|
47
|
+
source[shared] === target[shared]
|
|
48
|
+
) {
|
|
49
|
+
shared++
|
|
50
|
+
}
|
|
51
|
+
return [
|
|
52
|
+
...Array.from({ length: source.length - shared }, () => ".."),
|
|
53
|
+
...target.slice(shared),
|
|
54
|
+
].join("/")
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function extname(value: string) {
|
|
58
|
+
const name = basename(value)
|
|
59
|
+
const index = name.lastIndexOf(".")
|
|
60
|
+
return index > 0 ? name.slice(index) : ""
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function resolve(base: string, target: string) {
|
|
64
|
+
return normalize(target.startsWith("/") ? target : join(base, target))
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export const posixPath = {
|
|
68
|
+
basename,
|
|
69
|
+
dirname,
|
|
70
|
+
extname,
|
|
71
|
+
join,
|
|
72
|
+
normalize,
|
|
73
|
+
relative,
|
|
74
|
+
resolve,
|
|
75
|
+
}
|