First pass - no tests yet, had to make some changes to how pre-processing works, as well as updating the string based on context, if there is any overlap between the helpers and context it will prefix the overlap with ./ - this means to look in context.
This commit is contained in:
parent
4623fd406e
commit
165eff2e5a
|
@ -1,17 +1,18 @@
|
||||||
import { Context, createContext, runInNewContext } from "vm"
|
import { Context, createContext, runInNewContext } from "vm"
|
||||||
import { create, TemplateDelegate } from "handlebars"
|
import { create, TemplateDelegate } from "handlebars"
|
||||||
import { registerAll, registerMinimum } from "./helpers/index"
|
import { registerAll, registerMinimum } from "./helpers/index"
|
||||||
import { preprocess, postprocess } from "./processors"
|
import { postprocess, preprocess } from "./processors"
|
||||||
import {
|
import {
|
||||||
atob,
|
atob,
|
||||||
btoa,
|
btoa,
|
||||||
isBackendService,
|
|
||||||
FIND_HBS_REGEX,
|
|
||||||
FIND_ANY_HBS_REGEX,
|
FIND_ANY_HBS_REGEX,
|
||||||
|
FIND_HBS_REGEX,
|
||||||
findDoubleHbsInstances,
|
findDoubleHbsInstances,
|
||||||
|
isBackendService,
|
||||||
|
prefixStrings,
|
||||||
} from "./utilities"
|
} from "./utilities"
|
||||||
import { convertHBSBlock } from "./conversion"
|
import { convertHBSBlock } from "./conversion"
|
||||||
import { setJSRunner, removeJSRunner } from "./helpers/javascript"
|
import { removeJSRunner, setJSRunner } from "./helpers/javascript"
|
||||||
|
|
||||||
import manifest from "./manifest.json"
|
import manifest from "./manifest.json"
|
||||||
import { ProcessOptions } from "./types"
|
import { ProcessOptions } from "./types"
|
||||||
|
@ -23,6 +24,7 @@ export { iifeWrapper } from "./iife"
|
||||||
|
|
||||||
const hbsInstance = create()
|
const hbsInstance = create()
|
||||||
registerAll(hbsInstance)
|
registerAll(hbsInstance)
|
||||||
|
const helperNames = Object.keys(hbsInstance.helpers)
|
||||||
const hbsInstanceNoHelpers = create()
|
const hbsInstanceNoHelpers = create()
|
||||||
registerMinimum(hbsInstanceNoHelpers)
|
registerMinimum(hbsInstanceNoHelpers)
|
||||||
const defaultOpts: ProcessOptions = {
|
const defaultOpts: ProcessOptions = {
|
||||||
|
@ -45,11 +47,20 @@ function testObject(object: any) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function findOverlappingHelpers(context: object) {
|
||||||
|
const contextKeys = Object.keys(context)
|
||||||
|
return contextKeys.filter(key => helperNames.includes(key))
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a HBS template function for a given string, and optionally caches it.
|
* Creates a HBS template function for a given string, and optionally caches it.
|
||||||
*/
|
*/
|
||||||
const templateCache: Record<string, TemplateDelegate<any>> = {}
|
const templateCache: Record<string, TemplateDelegate<any>> = {}
|
||||||
function createTemplate(string: string, opts?: ProcessOptions) {
|
function createTemplate(
|
||||||
|
string: string,
|
||||||
|
opts?: ProcessOptions,
|
||||||
|
context?: object
|
||||||
|
) {
|
||||||
opts = { ...defaultOpts, ...opts }
|
opts = { ...defaultOpts, ...opts }
|
||||||
|
|
||||||
// Finalising adds a helper, can't do this with no helpers
|
// Finalising adds a helper, can't do this with no helpers
|
||||||
|
@ -60,7 +71,25 @@ function createTemplate(string: string, opts?: ProcessOptions) {
|
||||||
return templateCache[key]
|
return templateCache[key]
|
||||||
}
|
}
|
||||||
|
|
||||||
string = preprocess(string, opts)
|
const overlappingHelpers = !opts?.noHelpers
|
||||||
|
? findOverlappingHelpers(context)
|
||||||
|
: []
|
||||||
|
|
||||||
|
string = preprocess(string, {
|
||||||
|
...opts,
|
||||||
|
disabledHelpers: overlappingHelpers,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (context && !opts?.noHelpers) {
|
||||||
|
if (overlappingHelpers.length > 0) {
|
||||||
|
for (let block of findHBSBlocks(string)) {
|
||||||
|
string = string.replace(
|
||||||
|
block,
|
||||||
|
prefixStrings(block, overlappingHelpers, "./")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Optionally disable built in HBS escaping
|
// Optionally disable built in HBS escaping
|
||||||
if (opts.noEscaping) {
|
if (opts.noEscaping) {
|
||||||
|
@ -70,6 +99,7 @@ function createTemplate(string: string, opts?: ProcessOptions) {
|
||||||
// This does not throw an error when template can't be fulfilled,
|
// This does not throw an error when template can't be fulfilled,
|
||||||
// have to try correct beforehand
|
// have to try correct beforehand
|
||||||
const instance = opts.noHelpers ? hbsInstanceNoHelpers : hbsInstance
|
const instance = opts.noHelpers ? hbsInstanceNoHelpers : hbsInstance
|
||||||
|
|
||||||
const template = instance.compile(string, {
|
const template = instance.compile(string, {
|
||||||
strict: false,
|
strict: false,
|
||||||
})
|
})
|
||||||
|
@ -171,7 +201,8 @@ export function processStringSync(
|
||||||
throw "Cannot process non-string types."
|
throw "Cannot process non-string types."
|
||||||
}
|
}
|
||||||
function process(stringPart: string) {
|
function process(stringPart: string) {
|
||||||
const template = createTemplate(stringPart, opts)
|
// context is needed to check for overlap between helpers and context
|
||||||
|
const template = createTemplate(stringPart, opts, context)
|
||||||
const now = Math.floor(Date.now() / 1000) * 1000
|
const now = Math.floor(Date.now() / 1000) * 1000
|
||||||
const processedString = template({
|
const processedString = template({
|
||||||
now: new Date(now).toISOString(),
|
now: new Date(now).toISOString(),
|
||||||
|
|
|
@ -29,9 +29,9 @@ export function preprocess(string: string, opts: ProcessOptions) {
|
||||||
processor => processor.name !== preprocessor.PreprocessorNames.FINALISE
|
processor => processor.name !== preprocessor.PreprocessorNames.FINALISE
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return process(string, processors, opts)
|
return process(string, processors, opts)
|
||||||
}
|
}
|
||||||
export function postprocess(string: string) {
|
export function postprocess(string: string) {
|
||||||
let processors = postprocessor.processors
|
return process(string, postprocessor.processors)
|
||||||
return process(string, processors)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,10 +13,12 @@ export const PreprocessorNames = {
|
||||||
class Preprocessor {
|
class Preprocessor {
|
||||||
name: string
|
name: string
|
||||||
private fn: any
|
private fn: any
|
||||||
|
private helperNames: string[]
|
||||||
|
|
||||||
constructor(name: string, fn: any) {
|
constructor(name: string, fn: any) {
|
||||||
this.name = name
|
this.name = name
|
||||||
this.fn = fn
|
this.fn = fn
|
||||||
|
this.helperNames = HelperNames()
|
||||||
}
|
}
|
||||||
|
|
||||||
process(fullString: string, statement: string, opts: Object) {
|
process(fullString: string, statement: string, opts: Object) {
|
||||||
|
@ -56,7 +58,10 @@ export const processors = [
|
||||||
}),
|
}),
|
||||||
new Preprocessor(
|
new Preprocessor(
|
||||||
PreprocessorNames.FINALISE,
|
PreprocessorNames.FINALISE,
|
||||||
(statement: string, opts: { noHelpers: any }) => {
|
(
|
||||||
|
statement: string,
|
||||||
|
opts: { noHelpers: any; disabledHelpers?: string[] }
|
||||||
|
) => {
|
||||||
const noHelpers = opts && opts.noHelpers
|
const noHelpers = opts && opts.noHelpers
|
||||||
let insideStatement = statement.slice(2, statement.length - 2)
|
let insideStatement = statement.slice(2, statement.length - 2)
|
||||||
if (insideStatement.charAt(0) === " ") {
|
if (insideStatement.charAt(0) === " ") {
|
||||||
|
@ -75,7 +80,8 @@ export const processors = [
|
||||||
const testHelper = possibleHelper.trim().toLowerCase()
|
const testHelper = possibleHelper.trim().toLowerCase()
|
||||||
if (
|
if (
|
||||||
!noHelpers &&
|
!noHelpers &&
|
||||||
HelperNames().some(option => testHelper === option.toLowerCase())
|
!opts.disabledHelpers?.includes(testHelper) &&
|
||||||
|
this.helperNames.some(option => testHelper === option.toLowerCase())
|
||||||
) {
|
) {
|
||||||
insideStatement = `(${insideStatement})`
|
insideStatement = `(${insideStatement})`
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,4 +5,5 @@ export interface ProcessOptions {
|
||||||
noFinalise?: boolean
|
noFinalise?: boolean
|
||||||
escapeNewlines?: boolean
|
escapeNewlines?: boolean
|
||||||
onlyFound?: boolean
|
onlyFound?: boolean
|
||||||
|
disabledHelpers?: string[]
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,3 +66,16 @@ export const btoa = (plainText: string) => {
|
||||||
export const atob = (base64: string) => {
|
export const atob = (base64: string) => {
|
||||||
return Buffer.from(base64, "base64").toString("utf-8")
|
return Buffer.from(base64, "base64").toString("utf-8")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const prefixStrings = (
|
||||||
|
baseString: string,
|
||||||
|
strings: string[],
|
||||||
|
prefix: string
|
||||||
|
) => {
|
||||||
|
// Escape any special characters in the strings to avoid regex errors
|
||||||
|
const escapedStrings = strings.map(str =>
|
||||||
|
str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
|
||||||
|
)
|
||||||
|
const regexPattern = new RegExp(`\\b(${escapedStrings.join("|")})\\b`, "g")
|
||||||
|
return baseString.replace(regexPattern, `${prefix}$1`)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue