Comparing version 1.7.4 to 1.7.5
@@ -8,2 +8,10 @@ # Change Log | ||
## [1.7.5] - 2024-05-15 | ||
### Changed | ||
- `SsgContext.read(filename)` becomes `SsgContext.getInputFrom(filename)` to denote it affects the context's `inputFile`. | ||
- `SsgContext.readOrNew(filename, dir)` becomes `SsgContext.setOutputFrom(filename)` to denote it affects the context's `outputFile`. | ||
- `HtmlSsgFile.create(fileInfo, fileContents)` becomes `HtmlSsgFile.create(fileInfo)` to denote it uses `fileInfo`'s `contents`. | ||
## [1.7.4] - 2024-04-13 | ||
@@ -38,10 +46,7 @@ | ||
### Added | ||
`ReplaceCommand.contentStepEnd()` callback when the relevant ContentStep is terminating. | ||
## [1.5.2] - 2023-11-02 | ||
### Fixed | ||
`FileUtil.ssgCopy()` removed `cpy` dependency which has a bug causing output dir not always applied. | ||
@@ -52,3 +57,2 @@ | ||
### Fixed | ||
Replaced jest by testscript | ||
@@ -55,0 +59,0 @@ |
@@ -1,4 +0,4 @@ | ||
import { SsgContext } from './SsgContext.js'; | ||
import { SsgStep } from './step/index.js'; | ||
import { SsgConfig } from './SsgConfig'; | ||
import { SsgContext } from "./SsgContext.js"; | ||
import { SsgConfig } from "./SsgConfig"; | ||
import { SsgStep } from "./step"; | ||
/** | ||
@@ -5,0 +5,0 @@ * Result of a Ssg.start(). |
@@ -1,3 +0,3 @@ | ||
import { SsgFile } from "./util/index.js"; | ||
import { Logger } from "./Logger.js"; | ||
import { SsgFile } from "./util"; | ||
export interface SsgContext<V = any> extends Logger { | ||
@@ -39,4 +39,14 @@ /** | ||
pop(): SsgContext; | ||
read(fileName: string): void; | ||
readOrNew(fileName: string, outDir: string): void; | ||
/** | ||
* Reads a file and assign it to the context input. | ||
* | ||
* @param fileName | ||
*/ | ||
getInputFrom(fileName: string): SsgFile; | ||
/** | ||
* Reads or create (if not found) a file and assign it to the context output. | ||
* | ||
* By default (if the file is not found), output contents are equal to input contents. | ||
*/ | ||
setOutputFrom(filePath: string): SsgFile; | ||
} |
@@ -1,4 +0,4 @@ | ||
import { SsgFile } from "./util/index.js"; | ||
import { SsgContext } from "./SsgContext.js"; | ||
import { Logger } from "./Logger.js"; | ||
import { SsgFile } from "./util"; | ||
export declare class SsgContextImpl<V = any> implements SsgContext<V> { | ||
@@ -42,5 +42,11 @@ readonly locale: string; | ||
pop(): SsgContext; | ||
/** | ||
* Reads a file in this context. | ||
* | ||
* @param fileName The name of the file. | ||
* @protected | ||
*/ | ||
protected readFile(fileName: string): SsgFile; | ||
read(fileName: string): void; | ||
readOrNew(fileName: string, outDir: string): void; | ||
getInputFrom(filePath: string): SsgFile; | ||
setOutputFrom(filePath: string): SsgFile; | ||
} |
@@ -1,5 +0,4 @@ | ||
import { SsgFile, HtmlSsgFile } from "./util/index.js"; | ||
import { ObjectUtil } from "./util/ObjectUtil.js"; | ||
import { ConsoleLogger } from "./ConsoleLogger"; | ||
import path from "path"; | ||
import { HtmlSsgFile, SsgFile } from "./util"; | ||
export class SsgContextImpl { | ||
@@ -89,2 +88,8 @@ constructor(locale, vars = new Map(), name = SsgContextImpl.DEFAULT_NAME, logger = new ConsoleLogger(name), currentFile = undefined) { | ||
} | ||
/** | ||
* Reads a file in this context. | ||
* | ||
* @param fileName The name of the file. | ||
* @protected | ||
*/ | ||
readFile(fileName) { | ||
@@ -95,8 +100,8 @@ return fileName.endsWith(".html") | ||
} | ||
read(fileName) { | ||
this.inputFile = this.readFile(fileName); | ||
getInputFrom(filePath) { | ||
this.inputFile = this.readFile(filePath); | ||
return this.inputFile; | ||
} | ||
readOrNew(fileName, outDir) { | ||
setOutputFrom(filePath) { | ||
var _a; | ||
const filePath = path.join(outDir, fileName); | ||
const encoding = (_a = this._outputFile) === null || _a === void 0 ? void 0 : _a.encoding; | ||
@@ -109,9 +114,18 @@ let outFile; | ||
if (e.code === "ENOENT") { | ||
const lang = SsgFile.getLang(this, fileName); | ||
let lang; | ||
try { | ||
lang = SsgFile.getLang(this, filePath); | ||
} | ||
catch (e) { | ||
if (e.errno !== -2) { | ||
throw e; | ||
} | ||
lang = { lang: this.locale, variants: [] }; | ||
} | ||
if (filePath.endsWith(".html")) { | ||
const fileInfo = new SsgFile(filePath, encoding || "utf-8", "", new Date(), lang); | ||
outFile = HtmlSsgFile.create(fileInfo, ""); | ||
const fileInfo = new SsgFile(filePath, encoding || "utf-8", this.inputFile.contents, new Date(), lang); | ||
outFile = HtmlSsgFile.create(fileInfo); | ||
} | ||
else { | ||
outFile = new SsgFile(filePath, "utf8", "", new Date(), lang); | ||
outFile = new SsgFile(filePath, "utf8", this.inputFile.contents, new Date(), lang); | ||
} | ||
@@ -124,2 +138,3 @@ } | ||
this.outputFile = outFile; | ||
return this.outputFile; | ||
} | ||
@@ -126,0 +141,0 @@ } |
@@ -1,5 +0,5 @@ | ||
import { SsgStep } from '../SsgStep.js'; | ||
import { SsgContext } from '../../SsgContext.js'; | ||
import { ContentStepConfig } from './ContentStepConfig.js'; | ||
import { OutputFunc } from '../../OutputFunc'; | ||
import { SsgStep } from "../SsgStep.js"; | ||
import { SsgContext } from "../../SsgContext.js"; | ||
import { ContentStepConfig } from "./ContentStepConfig.js"; | ||
import { OutputFunc } from "../../OutputFunc"; | ||
export type ContentStepResult = { | ||
@@ -6,0 +6,0 @@ contentCount: number; |
@@ -1,4 +0,3 @@ | ||
import fs from 'fs'; | ||
import { promise as glob } from 'glob-promise'; | ||
import { HtmlSsgFile } from '../../util/index.js'; | ||
import fs from "fs"; | ||
import { promise as glob } from "glob-promise"; | ||
/** | ||
@@ -31,5 +30,3 @@ * A SsgStep that can perform replacements in files' contents. | ||
} | ||
return { | ||
contentCount | ||
}; | ||
return { contentCount }; | ||
} | ||
@@ -65,4 +62,4 @@ async processRoots(context, contentsConfig) { | ||
context.debug("Processing file", filePath); | ||
context.inputFile = HtmlSsgFile.read(context, filePath); | ||
context.outputFile = contentsConfig.getOutputFile(context); | ||
context.getInputFrom(filePath); | ||
context.setOutputFrom(contentsConfig.getOutputFile(context).name); | ||
const processed = this.shouldProcess(context); | ||
@@ -69,0 +66,0 @@ if (processed) { |
import { SsgStep } from "./SsgStep.js"; | ||
import { SsgContext } from "../SsgContext.js"; | ||
import { SsgConfig } from '../SsgConfig'; | ||
import { SsgConfig } from "../SsgConfig"; | ||
export interface DirectoryResult { | ||
@@ -13,9 +13,18 @@ directoryCount: number; | ||
export declare abstract class DirectoryStep<C extends SsgContext = SsgContext> implements SsgStep<C, DirectoryResult> { | ||
readonly dirs: string[]; | ||
readonly rootDirs: string[]; | ||
protected excludedDirs: string[]; | ||
protected template: string; | ||
protected templateFileName: string; | ||
protected config: SsgConfig; | ||
readonly name: string; | ||
constructor(dirs: string[], excludedDirs: string[], template: string, config: SsgConfig, name?: string); | ||
/** | ||
* Creates a new directory step. | ||
* | ||
* @param rootDirs A list of directories to look into. | ||
* @param excludedDirs A list of directories to avoid looking into. | ||
* @param templateFileName The name of the file containing the <--#echo var="directories"--> tag | ||
* @param config The SSG configuration. | ||
* @param name The step name ("directory" by default) | ||
*/ | ||
constructor(rootDirs: string[], excludedDirs: string[], templateFileName: string, config: SsgConfig, name?: string); | ||
/** | ||
* Execute the directory step by: | ||
@@ -34,4 +43,4 @@ * 1. finding all the subdirectories of each this.dirs | ||
protected abstract processDirs(context: SsgContext, dirames: string[]): Promise<void>; | ||
private findDirs; | ||
private findSubDirs; | ||
protected findDirs(fromDirs: string[]): Promise<string[]>; | ||
protected findSubDirs(ofDir: string): Promise<string[]>; | ||
} |
@@ -1,2 +0,3 @@ | ||
import { FileUtil } from "../util/index.js"; | ||
import path from "path"; | ||
import { FileUtil } from "../util"; | ||
/** | ||
@@ -8,6 +9,15 @@ * A step to enrich a template from some subdirectories processing. | ||
export class DirectoryStep { | ||
constructor(dirs, excludedDirs, template, config, name = "directory") { | ||
this.dirs = dirs; | ||
/** | ||
* Creates a new directory step. | ||
* | ||
* @param rootDirs A list of directories to look into. | ||
* @param excludedDirs A list of directories to avoid looking into. | ||
* @param templateFileName The name of the file containing the <--#echo var="directories"--> tag | ||
* @param config The SSG configuration. | ||
* @param name The step name ("directory" by default) | ||
*/ | ||
constructor(rootDirs, excludedDirs, templateFileName, config, name = "directory") { | ||
this.rootDirs = rootDirs; | ||
this.excludedDirs = excludedDirs; | ||
this.template = template; | ||
this.templateFileName = templateFileName; | ||
this.config = config; | ||
@@ -23,5 +33,6 @@ this.name = name; | ||
async execute(context) { | ||
context.read(this.template); | ||
context.readOrNew(this.template, this.config.outDir); | ||
const dirNames = (await this.findDirs(this.dirs)) | ||
context.getInputFrom(this.templateFileName); | ||
const outputFilePath = path.join(this.config.outDir, this.templateFileName); | ||
context.setOutputFrom(outputFilePath); | ||
const dirNames = (await this.findDirs(this.rootDirs)) | ||
.filter(dirName => !this.excludedDirs.includes(dirName)); | ||
@@ -28,0 +39,0 @@ await this.processDirs(context, dirNames); |
/// <reference types="node" /> | ||
import { SsgContext } from '../../SsgContext.js'; | ||
import { SsgFile, SsgFileLang } from './SsgFile.js'; | ||
import { JSDOM } from 'jsdom'; | ||
import { SsgContext } from "../../SsgContext.js"; | ||
import { SsgFile, SsgFileLang } from "./SsgFile.js"; | ||
import { JSDOM } from "jsdom"; | ||
export type HtmlMeta = { | ||
@@ -47,3 +47,10 @@ url?: string; | ||
get dom(): JSDOM; | ||
set dom(newDom: JSDOM); | ||
get document(): Document; | ||
get contents(): string; | ||
set contents(value: string); | ||
static read(context: SsgContext, fileName: string): HtmlSsgFile; | ||
static create(fileInfo: SsgFile): HtmlSsgFile; | ||
static getMeta(name: string, doc: Document): string[]; | ||
static getLink(rel: LinkType, doc: Document): Link | undefined; | ||
/** | ||
@@ -56,9 +63,2 @@ * Converts document's state to an HTML string. | ||
private updateMetaTags; | ||
set dom(newDom: JSDOM); | ||
get contents(): string; | ||
set contents(value: string); | ||
static read(context: SsgContext, fileName: string): HtmlSsgFile; | ||
static create(fileInfo: SsgFile, fileContents: string): HtmlSsgFile; | ||
static getMeta(name: string, doc: Document): string[]; | ||
static getLink(rel: LinkType, doc: Document): Link | undefined; | ||
} |
@@ -1,3 +0,3 @@ | ||
import { SsgFile } from './SsgFile.js'; | ||
import { JSDOM } from 'jsdom'; | ||
import { SsgFile } from "./SsgFile.js"; | ||
import { JSDOM } from "jsdom"; | ||
export var LinkType; | ||
@@ -33,5 +33,58 @@ (function (LinkType) { | ||
} | ||
set dom(newDom) { | ||
this._contents = newDom.serialize(); | ||
this._dom = newDom; | ||
} | ||
get document() { | ||
return this.dom.window.document; | ||
} | ||
get contents() { | ||
return this._contents; | ||
} | ||
set contents(value) { | ||
this._dom = undefined; | ||
this._contents = value; | ||
} | ||
static read(context, fileName) { | ||
const fileInfo = super.read(context, fileName); | ||
return this.create(fileInfo); | ||
} | ||
static create(fileInfo) { | ||
const dom = new JSDOM(fileInfo.contents); | ||
let title; | ||
const doc = dom.window.document; | ||
let docLang = doc.documentElement.lang; | ||
if (docLang) { | ||
fileInfo.lang.lang = docLang; | ||
} | ||
let titleElem = doc.querySelector("title"); | ||
if (titleElem) { | ||
const elemTitle = titleElem.textContent ? titleElem.textContent.trim() : ""; | ||
const split = elemTitle.lastIndexOf(" - "); | ||
title = split > 0 ? elemTitle.substring(0, split) : elemTitle; | ||
title = title === null || title === void 0 ? void 0 : title.replace(/\s{2,}/g, " ").replace(/[\n\t]/, " "); | ||
} | ||
const url = HtmlSsgFile.getMeta("url", doc)[0]; | ||
const author = HtmlSsgFile.getMeta("author", doc); | ||
const copyright = HtmlSsgFile.getMeta("copyright", doc)[0]; | ||
const description = HtmlSsgFile.getMeta("description", doc)[0]; | ||
const generator = HtmlSsgFile.getMeta("generator", doc)[0] || HtmlSsgFile.generator; | ||
const meta = { url, author, copyright, description, generator }; | ||
const start = HtmlSsgFile.getLink(LinkType.start, doc); | ||
const contents = HtmlSsgFile.getLink(LinkType.contents, doc); | ||
const prev = HtmlSsgFile.getLink(LinkType.prev, doc); | ||
const next = HtmlSsgFile.getLink(LinkType.next, doc); | ||
const links = { start, contents, prev, next }; | ||
return new HtmlSsgFile(fileInfo.name, fileInfo.encoding, fileInfo.contents, fileInfo.lastModified, fileInfo.lang, meta, links, title); | ||
} | ||
static getMeta(name, doc) { | ||
const metaElems = doc.querySelectorAll(`meta[name='${name}']`); | ||
return Array.from(metaElems).map(metaElem => metaElem.content); | ||
} | ||
static getLink(rel, doc) { | ||
const linkElem = doc.querySelector(`link[rel='${rel}']`); | ||
if (linkElem) { | ||
return { text: linkElem.title, url: linkElem.href, type: rel }; | ||
} | ||
} | ||
/** | ||
@@ -62,9 +115,9 @@ * Converts document's state to an HTML string. | ||
if (!linkElem) { | ||
linkElem = document.createElement('link'); | ||
linkElem.setAttribute('rel', rel); | ||
linkElem = document.createElement("link"); | ||
linkElem.setAttribute("rel", rel); | ||
document.head.append(linkElem); | ||
} | ||
linkElem.setAttribute('href', link.url); | ||
linkElem.setAttribute("href", link.url); | ||
if (link.text) { | ||
linkElem.setAttribute('title', link.text); | ||
linkElem.setAttribute("title", link.text); | ||
} | ||
@@ -77,3 +130,3 @@ } | ||
const meta = this.meta; | ||
meta.generator = meta.generator || 'ssg-api'; | ||
meta.generator = meta.generator || "ssg-api"; | ||
for (const metaName in meta) { | ||
@@ -85,65 +138,11 @@ const newContent = meta[metaName]; | ||
if (!metaElem) { | ||
metaElem = document.createElement('meta'); | ||
metaElem.setAttribute('name', metaName); | ||
metaElem = document.createElement("meta"); | ||
metaElem.setAttribute("name", metaName); | ||
document.head.append(metaElem); | ||
} | ||
metaElem.setAttribute('content', content); | ||
metaElem.setAttribute("content", content); | ||
} | ||
} | ||
} | ||
set dom(newDom) { | ||
this._contents = newDom.serialize(); | ||
this._dom = newDom; | ||
} | ||
get contents() { | ||
return this._contents; | ||
} | ||
set contents(value) { | ||
this._dom = undefined; | ||
this._contents = value; | ||
} | ||
static read(context, fileName) { | ||
const fileInfo = super.read(context, fileName); | ||
const fileContents = fileInfo.contents; | ||
return this.create(fileInfo, fileContents); | ||
} | ||
static create(fileInfo, fileContents) { | ||
const dom = new JSDOM(fileContents); | ||
let title; | ||
const doc = dom.window.document; | ||
let docLang = doc.documentElement.lang; | ||
if (docLang) { | ||
fileInfo.lang.lang = docLang; | ||
} | ||
let titleElem = doc.querySelector('title'); | ||
if (titleElem) { | ||
const elemTitle = titleElem.textContent ? titleElem.textContent.trim() : ''; | ||
const split = elemTitle.lastIndexOf(' - '); | ||
title = split > 0 ? elemTitle.substring(0, split) : elemTitle; | ||
title = title === null || title === void 0 ? void 0 : title.replace(/\s{2,}/g, ' ').replace(/[\n\t]/, ' '); | ||
} | ||
const url = HtmlSsgFile.getMeta('url', doc)[0]; | ||
const author = HtmlSsgFile.getMeta('author', doc); | ||
const copyright = HtmlSsgFile.getMeta('copyright', doc)[0]; | ||
const description = HtmlSsgFile.getMeta('description', doc)[0]; | ||
const generator = HtmlSsgFile.getMeta('generator', doc)[0] || HtmlSsgFile.generator; | ||
const meta = { url, author, copyright, description, generator }; | ||
const start = HtmlSsgFile.getLink(LinkType.start, doc); | ||
const contents = HtmlSsgFile.getLink(LinkType.contents, doc); | ||
const prev = HtmlSsgFile.getLink(LinkType.prev, doc); | ||
const next = HtmlSsgFile.getLink(LinkType.next, doc); | ||
const links = { start, contents, prev, next }; | ||
return new HtmlSsgFile(fileInfo.name, fileInfo.encoding, fileInfo.contents, fileInfo.lastModified, fileInfo.lang, meta, links, title); | ||
} | ||
static getMeta(name, doc) { | ||
const metaElems = doc.querySelectorAll(`meta[name='${name}']`); | ||
return Array.from(metaElems).map(metaElem => metaElem.content); | ||
} | ||
static getLink(rel, doc) { | ||
const linkElem = doc.querySelector(`link[rel='${rel}']`); | ||
if (linkElem) { | ||
return { text: linkElem.title, url: linkElem.href, type: rel }; | ||
} | ||
} | ||
} | ||
HtmlSsgFile.generator = 'ssg-api'; | ||
HtmlSsgFile.generator = "ssg-api"; |
@@ -5,3 +5,3 @@ { | ||
"author": "Jérôme Beau <javarome@gmail.com> (https://javarome.com)", | ||
"version": "1.7.4", | ||
"version": "1.7.5", | ||
"description": "Static Site Generation TypeScript API", | ||
@@ -8,0 +8,0 @@ "exports": "./dist/src/index.js", |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
84430
2100