mirror of
https://github.com/harttle/liquidjs.git
synced 2026-09-15 04:10:40 -07:00
Changes including: - will not call fs.resolve when normalizing `fs` - removed hardcoded `/` - mandatory `fs.dirname` for relative reference - mandatory `fs.sep`, defaults to '/'
This commit is contained in:
@@ -7,12 +7,12 @@ import { stringify } from '../../util/underscore'
|
||||
import { assert } from '../../util/assert'
|
||||
|
||||
export function append (v: string, arg: string) {
|
||||
assert(arguments.length === 2, () => 'append expect 2 arguments')
|
||||
assert(arguments.length === 2, 'append expect 2 arguments')
|
||||
return stringify(v) + stringify(arg)
|
||||
}
|
||||
|
||||
export function prepend (v: string, arg: string) {
|
||||
assert(arguments.length === 2, () => 'prepend expect 2 arguments')
|
||||
assert(arguments.length === 2, 'prepend expect 2 arguments')
|
||||
return stringify(arg) + stringify(v)
|
||||
}
|
||||
|
||||
|
||||
@@ -60,3 +60,9 @@ export async function exists (filepath: string) {
|
||||
export function existsSync (filepath: string) {
|
||||
return true
|
||||
}
|
||||
|
||||
export function dirname (filepath: string) {
|
||||
return domResolve(filepath, '.')
|
||||
}
|
||||
|
||||
export const sep = '/'
|
||||
|
||||
+14
-2
@@ -1,8 +1,20 @@
|
||||
import { LoaderOptions } from './loader'
|
||||
|
||||
export interface FS {
|
||||
/** check if a file exists asynchronously */
|
||||
exists: (filepath: string) => Promise<boolean>;
|
||||
readFile: (filepath: string) => Promise<string>;
|
||||
/** check if a file exists synchronously */
|
||||
existsSync: (filepath: string) => boolean;
|
||||
/** read a file asynchronously */
|
||||
readFile: (filepath: string) => Promise<string>;
|
||||
/** read a file synchronously */
|
||||
readFileSync: (filepath: string) => string;
|
||||
resolve: (root: string, file: string, ext: string) => string;
|
||||
/** resolve a file against directory, for given `ext` option */
|
||||
resolve: (dir: string, file: string, ext: string, options?: LoaderOptions) => string;
|
||||
/** defaults to "/", will be used for "within roots" check */
|
||||
sep?: string;
|
||||
/** dirname for a filepath, used when resolving relative path */
|
||||
dirname?: (file: string) => string;
|
||||
/** fallback file for lookup failure */
|
||||
fallback?: (file: string) => string | undefined;
|
||||
}
|
||||
|
||||
+22
-15
@@ -1,6 +1,8 @@
|
||||
import { FS } from './fs'
|
||||
import { escapeRegex } from '../util/underscore'
|
||||
import { assert } from '../util/assert'
|
||||
|
||||
interface LoaderOptions {
|
||||
export interface LoaderOptions {
|
||||
fs: FS;
|
||||
extname: string;
|
||||
root: string[];
|
||||
@@ -15,9 +17,13 @@ export enum LookupType {
|
||||
}
|
||||
export class Loader {
|
||||
private options: LoaderOptions
|
||||
private sep: string
|
||||
private rRelativePath: RegExp
|
||||
|
||||
constructor (options: LoaderOptions) {
|
||||
this.options = options
|
||||
this.sep = this.options.fs.sep || '/'
|
||||
this.rRelativePath = new RegExp(['.' + this.sep, '..' + this.sep].map(prefix => escapeRegex(prefix)).join('|'))
|
||||
}
|
||||
|
||||
public * lookup (file: string, type: LookupType, sync?: boolean, currentFile?: string) {
|
||||
@@ -29,20 +35,12 @@ export class Loader {
|
||||
throw this.lookupError(file, dirs)
|
||||
}
|
||||
|
||||
public shouldLoadRelative (currentFile: string) {
|
||||
return this.options.relativeReference && this.isRelativePath(currentFile)
|
||||
}
|
||||
|
||||
public isRelativePath (path: string) {
|
||||
return path.startsWith('./') || path.startsWith('../')
|
||||
}
|
||||
|
||||
public * candidates (file: string, dirs: string[], currentFile?: string, enforceRoot?: boolean) {
|
||||
const { fs, extname } = this.options
|
||||
if (this.shouldLoadRelative(file) && currentFile) {
|
||||
const referenced = fs.resolve(this.dirname(currentFile), file, extname)
|
||||
const referenced = fs.resolve(this.dirname(currentFile), file, extname, this.options)
|
||||
for (const dir of dirs) {
|
||||
if (!enforceRoot || referenced.startsWith(dir)) {
|
||||
if (!enforceRoot || this.withinDir(referenced, dir)) {
|
||||
// the relatively referenced file is within one of root dirs
|
||||
yield referenced
|
||||
break
|
||||
@@ -51,7 +49,7 @@ export class Loader {
|
||||
}
|
||||
for (const dir of dirs) {
|
||||
const referenced = fs.resolve(dir, file, extname)
|
||||
if (!enforceRoot || referenced.startsWith(dir)) {
|
||||
if (!enforceRoot || this.withinDir(referenced, dir)) {
|
||||
yield referenced
|
||||
}
|
||||
}
|
||||
@@ -61,10 +59,19 @@ export class Loader {
|
||||
}
|
||||
}
|
||||
|
||||
private withinDir (file: string, dir: string) {
|
||||
dir = dir.endsWith(this.sep) ? dir : dir + this.sep
|
||||
return file.startsWith(dir)
|
||||
}
|
||||
|
||||
private shouldLoadRelative (referencedFile: string) {
|
||||
return this.options.relativeReference && this.rRelativePath.test(referencedFile)
|
||||
}
|
||||
|
||||
private dirname (path: string) {
|
||||
const segments = path.split('/')
|
||||
segments.pop()
|
||||
return segments.join('/')
|
||||
const fs = this.options.fs
|
||||
assert(fs.dirname, '`fs.dirname` is required for relative reference')
|
||||
return fs.dirname!(path)
|
||||
}
|
||||
|
||||
private lookupError (file: string, roots: string[]) {
|
||||
|
||||
+7
-2
@@ -1,6 +1,7 @@
|
||||
import * as _ from '../util/underscore'
|
||||
import { resolve as nodeResolve, extname } from 'path'
|
||||
import { resolve as nodeResolve, extname, dirname as nodeDirname } from 'path'
|
||||
import { stat, statSync, readFile as nodeReadFile, readFileSync as nodeReadFileSync } from 'fs'
|
||||
import { LiquidOptions } from '../liquid-options'
|
||||
|
||||
const statAsync = _.promisify(stat)
|
||||
const readFileAsync = _.promisify<string, string, string>(nodeReadFile)
|
||||
@@ -27,7 +28,7 @@ export function existsSync (filepath: string) {
|
||||
export function readFileSync (filepath: string) {
|
||||
return nodeReadFileSync(filepath, 'utf8')
|
||||
}
|
||||
export function resolve (root: string, file: string, ext: string) {
|
||||
export function resolve (root: string, file: string, ext: string, opts: LiquidOptions) {
|
||||
if (!extname(file)) file += ext
|
||||
return nodeResolve(root, file)
|
||||
}
|
||||
@@ -36,3 +37,7 @@ export function fallback (file: string) {
|
||||
return require.resolve(file)
|
||||
} catch (e) {}
|
||||
}
|
||||
export function dirname (filepath: string) {
|
||||
return nodeDirname(filepath)
|
||||
}
|
||||
export { sep } from 'path'
|
||||
|
||||
@@ -169,5 +169,5 @@ export function normalizeDirectoryList (value: any): string[] {
|
||||
let list: string[] = []
|
||||
if (_.isArray(value)) list = value
|
||||
if (_.isString(value)) list = [value]
|
||||
return list.map(str => fs.resolve(str, '.', '')).map(str => str[str.length - 1] !== '/' ? str + '/' : str)
|
||||
return list
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ export class Expression {
|
||||
this.postfix = [...toPostfix(tokens)]
|
||||
}
|
||||
public * evaluate (ctx: Context, lenient: boolean): any {
|
||||
assert(ctx, () => 'unable to evaluate: context not defined')
|
||||
assert(ctx, 'unable to evaluate: context not defined')
|
||||
const operands: any[] = []
|
||||
for (const token of this.postfix) {
|
||||
if (TypeGuards.isOperatorToken(token)) {
|
||||
|
||||
+4
-2
@@ -1,8 +1,10 @@
|
||||
import { AssertionError } from './error'
|
||||
|
||||
export function assert <T> (predicate: T | null | undefined, message?: () => string) {
|
||||
export function assert <T> (predicate: T | null | undefined, message?: string | (() => string)) {
|
||||
if (!predicate) {
|
||||
const msg = message ? message() : `expect ${predicate} to be true`
|
||||
const msg = typeof message === 'function'
|
||||
? message()
|
||||
: (message || `expect ${predicate} to be true`)
|
||||
throw new AssertionError(msg)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,10 @@ export function isFunction (value: any): value is Function {
|
||||
return typeof value === 'function'
|
||||
}
|
||||
|
||||
export function escapeRegex (str: string) {
|
||||
return str.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&')
|
||||
}
|
||||
|
||||
export function promisify<T1, T2> (fn: (arg1: T1, cb: (err: Error | null, result: T2) => void) => void): (arg1: T1) => Promise<T2>;
|
||||
export function promisify<T1, T2, T3> (fn: (arg1: T1, arg2: T2, cb: (err: Error | null, result: T3) => void) => void): (arg1: T1, arg2: T2) => Promise<T3>;
|
||||
export function promisify (fn: any) {
|
||||
|
||||
Reference in New Issue
Block a user