@projitect/blueprint 0.0.0-stage → 1.0.1
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 +21 -0
- package/dist/add-sections.d.ts +87 -0
- package/dist/add-sections.d.ts.map +1 -0
- package/dist/add-sections.js +60 -0
- package/dist/add-sections.js.map +1 -0
- package/dist/constructors.d.ts +103 -0
- package/dist/constructors.d.ts.map +1 -0
- package/dist/constructors.js +88 -0
- package/dist/constructors.js.map +1 -0
- package/dist/detect.d.ts +19 -0
- package/dist/detect.d.ts.map +1 -0
- package/dist/detect.js +36 -0
- package/dist/detect.js.map +1 -0
- package/dist/directory.d.ts +17 -0
- package/dist/directory.d.ts.map +1 -0
- package/dist/directory.js +8 -0
- package/dist/directory.js.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -0
- package/dist/sections.d.ts +75 -0
- package/dist/sections.d.ts.map +1 -0
- package/dist/sections.js +85 -0
- package/dist/sections.js.map +1 -0
- package/dist/splice.d.ts +23 -0
- package/dist/splice.d.ts.map +1 -0
- package/dist/splice.js +78 -0
- package/dist/splice.js.map +1 -0
- package/package.json +41 -3
- package/src/add-sections.ts +140 -0
- package/src/constructors.ts +202 -0
- package/src/detect.ts +48 -0
- package/src/directory.ts +30 -0
- package/src/index.ts +21 -0
- package/src/sections.ts +99 -0
- package/src/splice.ts +99 -0
- package/README.md +0 -3
package/src/index.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @projitect/blueprint
|
|
3
|
+
*
|
|
4
|
+
* Authoring SDK for blueprint packages. Blueprint authors compose these primitives — the
|
|
5
|
+
* `BlueprintFileSystem` service for FS access, the four ownership-mode constructors
|
|
6
|
+
* (`regionFile` / `jsonMerge` / `ownFile` / `seedFile`), the post-apply `exec` constructor, the section helpers
|
|
7
|
+
* (`markdownSection`, `ignoreSection`), the `directory` scope transform, and detectors for
|
|
8
|
+
* common project introspection.
|
|
9
|
+
*
|
|
10
|
+
* Authors use Effect's own control-flow combinators (`Effect.gen`, `Effect.all`, `Effect.if`,
|
|
11
|
+
* `Effect.when`, `Effect.unless`, `Effect.forEach`) for sequencing and conditionals — we do
|
|
12
|
+
* not ship duplicates.
|
|
13
|
+
*/
|
|
14
|
+
export * from "./constructors.js"
|
|
15
|
+
export * from "./sections.js"
|
|
16
|
+
export * from "./directory.js"
|
|
17
|
+
export * from "./detect.js"
|
|
18
|
+
export * from "./splice.js"
|
|
19
|
+
export * from "./add-sections.js"
|
|
20
|
+
export { BlueprintFileSystem } from "@projitect/core"
|
|
21
|
+
export { Blueprint, ChangeSet, Permission, Errors } from "@projitect/core"
|
package/src/sections.ts
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Section helpers — thin wrappers around `regionFile` that bake in the right comment
|
|
3
|
+
* delimiters for two common file shapes:
|
|
4
|
+
*
|
|
5
|
+
* - **`markdownSection`** for HTML/MDX/XML files where comments need a closing `-->`.
|
|
6
|
+
* The fenced region renders invisible in markdown output — readers never see the markers.
|
|
7
|
+
* Useful for README.md, AGENTS.md, CONTRIBUTING.md, generated MDX docs, etc.
|
|
8
|
+
*
|
|
9
|
+
* - **`ignoreSection`** for `#`-comment "ignore"-style files. Same shape as the gitignore
|
|
10
|
+
* convention; the helper exists because the name documents intent better than a bare
|
|
11
|
+
* `regionFile({ commentPrefix: "#" })` does, and the surface guides authors toward the
|
|
12
|
+
* right primitive when they're scaffolding a `.eslintignore` / `.prettierignore` /
|
|
13
|
+
* `.dockerignore` / `.npmignore` blueprint.
|
|
14
|
+
*
|
|
15
|
+
* Both helpers re-emit the underlying `RegionFileSpec`-shaped result of `regionFile`, so
|
|
16
|
+
* everything that works for `regionFile` works for these (extra permissions, descriptions,
|
|
17
|
+
* etc.). They aren't shipped as separate npm packages because there's no opinionated content
|
|
18
|
+
* to bundle — the blueprint author always provides their own.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { StructX } from "@nunofyobiz/effect-extras"
|
|
22
|
+
import type { Blueprint, Permission } from "@projitect/core"
|
|
23
|
+
import { regionFile } from "./constructors.js"
|
|
24
|
+
|
|
25
|
+
export interface SectionSpec {
|
|
26
|
+
readonly id: string
|
|
27
|
+
readonly version: string
|
|
28
|
+
readonly description?: string
|
|
29
|
+
readonly path: string
|
|
30
|
+
readonly content: string
|
|
31
|
+
readonly extraPermissions?: readonly Permission.Permission[]
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Region-mode blueprint for HTML / MDX / XML files. The fence is rendered as two HTML
|
|
36
|
+
* comments on their own lines:
|
|
37
|
+
*
|
|
38
|
+
* <!-- pjt:<owner-id> start -->
|
|
39
|
+
* <body>
|
|
40
|
+
* <!-- pjt:<owner-id> end -->
|
|
41
|
+
*
|
|
42
|
+
* The markers don't render in HTML output (they're comments), so readers of the published
|
|
43
|
+
* file never see them. `pjt inspect` flags hand-edits between the markers; lines outside
|
|
44
|
+
* the fence belong to the user.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```ts
|
|
48
|
+
* markdownSection({
|
|
49
|
+
* id: "my-org:readme:badges",
|
|
50
|
+
* version: "1.0.0",
|
|
51
|
+
* path: "README.md",
|
|
52
|
+
* content: "\n",
|
|
53
|
+
* })
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
export const markdownSection = (spec: SectionSpec): Blueprint.Blueprint =>
|
|
57
|
+
regionFile({
|
|
58
|
+
id: spec.id,
|
|
59
|
+
version: spec.version,
|
|
60
|
+
...StructX.defined("description", spec.description),
|
|
61
|
+
path: spec.path,
|
|
62
|
+
commentPrefix: "<!--",
|
|
63
|
+
commentSuffix: " -->",
|
|
64
|
+
content: spec.content,
|
|
65
|
+
...StructX.defined("extraPermissions", spec.extraPermissions),
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Region-mode blueprint for `#`-comment ignore-style files: `.gitignore`, `.eslintignore`,
|
|
70
|
+
* `.prettierignore`, `.dockerignore`, `.npmignore`, etc. The fence is two `#`-prefixed lines
|
|
71
|
+
* on either side of the section content:
|
|
72
|
+
*
|
|
73
|
+
* # pjt:<owner-id> start
|
|
74
|
+
* <body>
|
|
75
|
+
* # pjt:<owner-id> end
|
|
76
|
+
*
|
|
77
|
+
* Equivalent to `regionFile({ commentPrefix: "#", ... })`, but the name documents the
|
|
78
|
+
* intent and makes the right primitive easy to find.
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* ```ts
|
|
82
|
+
* ignoreSection({
|
|
83
|
+
* id: "my-org:eslintignore:generated",
|
|
84
|
+
* version: "1.0.0",
|
|
85
|
+
* path: ".eslintignore",
|
|
86
|
+
* content: "dist/\nbuild/\ncoverage/\n",
|
|
87
|
+
* })
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
export const ignoreSection = (spec: SectionSpec): Blueprint.Blueprint =>
|
|
91
|
+
regionFile({
|
|
92
|
+
id: spec.id,
|
|
93
|
+
version: spec.version,
|
|
94
|
+
...StructX.defined("description", spec.description),
|
|
95
|
+
path: spec.path,
|
|
96
|
+
commentPrefix: "#",
|
|
97
|
+
content: spec.content,
|
|
98
|
+
...StructX.defined("extraPermissions", spec.extraPermissions),
|
|
99
|
+
})
|
package/src/splice.ts
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { Effect } from "effect"
|
|
2
|
+
import { Errors } from "@projitect/core"
|
|
3
|
+
import { StringX } from "@nunofyobiz/effect-extras"
|
|
4
|
+
|
|
5
|
+
const IMPORTS_START = "// pjt:imports start"
|
|
6
|
+
const IMPORTS_END = "// pjt:imports end"
|
|
7
|
+
const BLUEPRINTS_START = "// pjt:blueprints start"
|
|
8
|
+
const BLUEPRINTS_END = "// pjt:blueprints end"
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Splice an import line and one or more blueprint call lines into `.pjt.ts` text using the
|
|
12
|
+
* convention markers. The import goes between `// pjt:imports start/end` (deduped against
|
|
13
|
+
* existing content), the calls go between `// pjt:blueprints start/end` (appended, with 4-space
|
|
14
|
+
* indent to match the standard template). An empty `callLines` leaves the blueprints block alone.
|
|
15
|
+
*
|
|
16
|
+
* Pure string-to-string: the caller owns reading and writing the file. `blueprintFile` is only
|
|
17
|
+
* used to name the file in the error message.
|
|
18
|
+
*
|
|
19
|
+
* Fails with `pjt.add.markers-missing` if either marker pair is missing — the user has
|
|
20
|
+
* hand-edited the file and we won't guess.
|
|
21
|
+
*/
|
|
22
|
+
export const spliceBlueprintFile = (params: {
|
|
23
|
+
readonly content: string
|
|
24
|
+
readonly blueprintFile: string
|
|
25
|
+
/** Import statement to splice (one line, no trailing newline). Deduped. */
|
|
26
|
+
readonly importLine: string
|
|
27
|
+
/** Blueprint call lines (one entry per line, no leading indent, no trailing newline). */
|
|
28
|
+
readonly callLines: readonly string[]
|
|
29
|
+
}): Effect.Effect<string, Errors.AddMarkersMissing> =>
|
|
30
|
+
Effect.gen(function* () {
|
|
31
|
+
const afterImport = yield* spliceImport({
|
|
32
|
+
content: params.content,
|
|
33
|
+
blueprintFile: params.blueprintFile,
|
|
34
|
+
importLine: params.importLine,
|
|
35
|
+
})
|
|
36
|
+
return yield* spliceCalls({
|
|
37
|
+
content: afterImport,
|
|
38
|
+
blueprintFile: params.blueprintFile,
|
|
39
|
+
callLines: params.callLines,
|
|
40
|
+
})
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
const spliceImport = (params: {
|
|
44
|
+
readonly content: string
|
|
45
|
+
readonly blueprintFile: string
|
|
46
|
+
readonly importLine: string
|
|
47
|
+
}): Effect.Effect<string, Errors.AddMarkersMissing> => {
|
|
48
|
+
const { content, blueprintFile, importLine } = params
|
|
49
|
+
return Effect.gen(function* () {
|
|
50
|
+
const lines = content.split("\n")
|
|
51
|
+
const startIndex = lines.findIndex((l) => l.trim() === IMPORTS_START)
|
|
52
|
+
const endIndex = lines.findIndex((l) => l.trim() === IMPORTS_END)
|
|
53
|
+
if (startIndex === -1 || endIndex === -1 || endIndex <= startIndex) {
|
|
54
|
+
return yield* new Errors.AddMarkersMissing({
|
|
55
|
+
id: "pjt.add.markers-missing",
|
|
56
|
+
blueprintFile,
|
|
57
|
+
missingMarker: `${IMPORTS_START} / ${IMPORTS_END}`,
|
|
58
|
+
message:
|
|
59
|
+
`${blueprintFile} is missing the \`pjt:imports start\` / \`pjt:imports end\` markers. ` +
|
|
60
|
+
"Restore them (see `pjt init`'s starter template) and re-run `pjt add`.",
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
// Dedupe: skip if any line between the markers already equals the import
|
|
64
|
+
const between = lines.slice(startIndex + 1, endIndex).map((l) => l.trim())
|
|
65
|
+
if (between.includes(importLine.trim())) {
|
|
66
|
+
return content
|
|
67
|
+
}
|
|
68
|
+
return StringX.insertBeforeLine(content, endIndex, [importLine])
|
|
69
|
+
})
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const spliceCalls = (params: {
|
|
73
|
+
readonly content: string
|
|
74
|
+
readonly blueprintFile: string
|
|
75
|
+
readonly callLines: readonly string[]
|
|
76
|
+
}): Effect.Effect<string, Errors.AddMarkersMissing> => {
|
|
77
|
+
const { content, blueprintFile, callLines } = params
|
|
78
|
+
return Effect.gen(function* () {
|
|
79
|
+
if (callLines.length === 0) {
|
|
80
|
+
return content
|
|
81
|
+
}
|
|
82
|
+
const lines = content.split("\n")
|
|
83
|
+
const startIndex = lines.findIndex((l) => l.trim() === BLUEPRINTS_START)
|
|
84
|
+
const endIndex = lines.findIndex((l) => l.trim() === BLUEPRINTS_END)
|
|
85
|
+
if (startIndex === -1 || endIndex === -1 || endIndex <= startIndex) {
|
|
86
|
+
return yield* new Errors.AddMarkersMissing({
|
|
87
|
+
id: "pjt.add.markers-missing",
|
|
88
|
+
blueprintFile,
|
|
89
|
+
missingMarker: `${BLUEPRINTS_START} / ${BLUEPRINTS_END}`,
|
|
90
|
+
message:
|
|
91
|
+
`${blueprintFile} is missing the \`pjt:blueprints start\` / \`pjt:blueprints end\` markers. ` +
|
|
92
|
+
"Restore them and re-run `pjt add`.",
|
|
93
|
+
})
|
|
94
|
+
}
|
|
95
|
+
const indent = " " // 4-space, matching the starter template
|
|
96
|
+
const newCalls = callLines.map((c) => `${indent}${c.replace(/^\s+/u, "")}`)
|
|
97
|
+
return StringX.insertBeforeLine(content, endIndex, newCalls)
|
|
98
|
+
})
|
|
99
|
+
}
|
package/README.md
DELETED