full template restoreablity
Some checks failed
Publish npm package / publish (push) Failing after 21s
Some checks failed
Publish npm package / publish (push) Failing after 21s
This commit is contained in:
104
reverse.ts
104
reverse.ts
@@ -8,7 +8,7 @@ import {
|
||||
} from "node:fs"
|
||||
import { dirname, isAbsolute, relative, resolve as resolvePath } from "node:path"
|
||||
import { TextDecoder } from "node:util"
|
||||
import type { ReverseMapFile, ReverseMapManifest, ReverseMapToken } from "./render"
|
||||
import type { ReverseMapFile, ReverseMapManifest, ReverseMapStoredTemplate, ReverseMapToken } from "./render"
|
||||
|
||||
export type ReverseOptions = {
|
||||
mapPath?: string
|
||||
@@ -71,17 +71,73 @@ function replaceFirst(content: string, token: ReverseMapToken): string | null {
|
||||
return `${content.slice(0, index)}${token.token}${content.slice(index + token.result.length)}`
|
||||
}
|
||||
|
||||
function applyActiveBranch(token: ReverseMapToken, branchContent: string): string {
|
||||
if (!token.activeRange) return token.token
|
||||
return [
|
||||
token.token.slice(0, token.activeRange.start),
|
||||
branchContent,
|
||||
token.token.slice(token.activeRange.end),
|
||||
].join("")
|
||||
}
|
||||
|
||||
function findPrefixEnd(content: string, before: string): number {
|
||||
if (before === "") return 0
|
||||
let candidate = before
|
||||
while (candidate.length >= 8) {
|
||||
const index = content.indexOf(candidate)
|
||||
if (index !== -1) return index + candidate.length
|
||||
candidate = candidate.slice(-Math.max(1, Math.floor(candidate.length / 2)))
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
function findSuffixStart(content: string, after: string, from: number): number {
|
||||
if (after === "") return content.length
|
||||
let candidate = after
|
||||
while (candidate.length >= 8) {
|
||||
const index = content.indexOf(candidate, from)
|
||||
if (index !== -1) return index
|
||||
candidate = candidate.slice(0, Math.floor(candidate.length / 2))
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
function replaceConditional(content: string, token: ReverseMapToken): string | null {
|
||||
const exactIndex = content.indexOf(token.result)
|
||||
if (exactIndex !== -1) {
|
||||
return [
|
||||
content.slice(0, exactIndex),
|
||||
token.token,
|
||||
content.slice(exactIndex + token.result.length),
|
||||
].join("")
|
||||
}
|
||||
|
||||
if (token.before === undefined || token.after === undefined) return null
|
||||
|
||||
const branchStart = findPrefixEnd(content, token.before)
|
||||
if (branchStart === -1) return null
|
||||
|
||||
const afterIndex = findSuffixStart(content, token.after, branchStart)
|
||||
if (afterIndex === -1) return null
|
||||
|
||||
return [
|
||||
content.slice(0, branchStart),
|
||||
applyActiveBranch(token, content.slice(branchStart, afterIndex)),
|
||||
content.slice(afterIndex),
|
||||
].join("")
|
||||
}
|
||||
|
||||
function reverseContent(
|
||||
content: string,
|
||||
file: ReverseMapFile,
|
||||
warnings: ReverseWarning[],
|
||||
): string {
|
||||
const tokens = file.tokens
|
||||
const contentTokens = file.tokens
|
||||
.filter(token => token.kind === "content")
|
||||
.sort((a, b) => (b.range?.start ?? -1) - (a.range?.start ?? -1))
|
||||
|
||||
let reversed = content
|
||||
for (const token of tokens) {
|
||||
for (const token of contentTokens) {
|
||||
const rangeResult = replaceAtRange(reversed, token)
|
||||
if (rangeResult !== null) {
|
||||
reversed = rangeResult
|
||||
@@ -101,9 +157,47 @@ function reverseContent(
|
||||
message: "Rendered value was not found; token was not restored",
|
||||
})
|
||||
}
|
||||
|
||||
const conditionalTokens = file.tokens
|
||||
.filter(token => token.kind === "conditional")
|
||||
.sort((a, b) => (b.range?.start ?? -1) - (a.range?.start ?? -1))
|
||||
|
||||
for (const token of conditionalTokens) {
|
||||
const result = replaceConditional(reversed, token)
|
||||
if (result !== null) {
|
||||
reversed = result
|
||||
continue
|
||||
}
|
||||
|
||||
warnings.push({
|
||||
outputPath: file.outputPath,
|
||||
token: token.token,
|
||||
result: token.result,
|
||||
message: "Rendered conditional block was not found; block was not restored",
|
||||
})
|
||||
}
|
||||
|
||||
return reversed
|
||||
}
|
||||
|
||||
function writeSkippedTemplate(
|
||||
templateRoot: string,
|
||||
skipped: ReverseMapStoredTemplate,
|
||||
): number {
|
||||
const templatePath = resolveInside(templateRoot, skipped.templatePath)
|
||||
if (skipped.kind === "directory") {
|
||||
mkdirSync(templatePath, { recursive: true })
|
||||
return 0
|
||||
}
|
||||
|
||||
mkdirSync(dirname(templatePath), { recursive: true })
|
||||
const content = skipped.encoding === "base64"
|
||||
? Buffer.from(skipped.content ?? "", "base64")
|
||||
: skipped.content ?? ""
|
||||
writeFileSync(templatePath, content)
|
||||
return 1
|
||||
}
|
||||
|
||||
export function reverseDir(
|
||||
renderedDir: string,
|
||||
templateDir: string,
|
||||
@@ -154,5 +248,9 @@ export function reverseDir(
|
||||
filesWritten += 1
|
||||
}
|
||||
|
||||
for (const skipped of manifest.skipped ?? []) {
|
||||
filesWritten += writeSkippedTemplate(templateRoot, skipped)
|
||||
}
|
||||
|
||||
return { filesWritten, warnings }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user