first commit
This commit is contained in:
commit
0ef48e85f0
175
.gitignore
vendored
Normal file
175
.gitignore
vendored
Normal file
|
@ -0,0 +1,175 @@
|
||||||
|
# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
|
||||||
|
logs
|
||||||
|
_.log
|
||||||
|
npm-debug.log_
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
.pnpm-debug.log*
|
||||||
|
|
||||||
|
# Caches
|
||||||
|
|
||||||
|
.cache
|
||||||
|
|
||||||
|
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||||
|
|
||||||
|
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
|
||||||
|
pids
|
||||||
|
_.pid
|
||||||
|
_.seed
|
||||||
|
*.pid.lock
|
||||||
|
|
||||||
|
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||||
|
|
||||||
|
lib-cov
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
|
||||||
|
coverage
|
||||||
|
*.lcov
|
||||||
|
|
||||||
|
# nyc test coverage
|
||||||
|
|
||||||
|
.nyc_output
|
||||||
|
|
||||||
|
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
|
|
||||||
|
.grunt
|
||||||
|
|
||||||
|
# Bower dependency directory (https://bower.io/)
|
||||||
|
|
||||||
|
bower_components
|
||||||
|
|
||||||
|
# node-waf configuration
|
||||||
|
|
||||||
|
.lock-wscript
|
||||||
|
|
||||||
|
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||||
|
|
||||||
|
build/Release
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
|
||||||
|
node_modules/
|
||||||
|
jspm_packages/
|
||||||
|
|
||||||
|
# Snowpack dependency directory (https://snowpack.dev/)
|
||||||
|
|
||||||
|
web_modules/
|
||||||
|
|
||||||
|
# TypeScript cache
|
||||||
|
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional eslint cache
|
||||||
|
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
# Optional stylelint cache
|
||||||
|
|
||||||
|
.stylelintcache
|
||||||
|
|
||||||
|
# Microbundle cache
|
||||||
|
|
||||||
|
.rpt2_cache/
|
||||||
|
.rts2_cache_cjs/
|
||||||
|
.rts2_cache_es/
|
||||||
|
.rts2_cache_umd/
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
|
||||||
|
.node_repl_history
|
||||||
|
|
||||||
|
# Output of 'npm pack'
|
||||||
|
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# Yarn Integrity file
|
||||||
|
|
||||||
|
.yarn-integrity
|
||||||
|
|
||||||
|
# dotenv environment variable files
|
||||||
|
|
||||||
|
.env
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
.env.local
|
||||||
|
|
||||||
|
# parcel-bundler cache (https://parceljs.org/)
|
||||||
|
|
||||||
|
.parcel-cache
|
||||||
|
|
||||||
|
# Next.js build output
|
||||||
|
|
||||||
|
.next
|
||||||
|
out
|
||||||
|
|
||||||
|
# Nuxt.js build / generate output
|
||||||
|
|
||||||
|
.nuxt
|
||||||
|
dist
|
||||||
|
|
||||||
|
# Gatsby files
|
||||||
|
|
||||||
|
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||||
|
|
||||||
|
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||||
|
|
||||||
|
# public
|
||||||
|
|
||||||
|
# vuepress build output
|
||||||
|
|
||||||
|
.vuepress/dist
|
||||||
|
|
||||||
|
# vuepress v2.x temp and cache directory
|
||||||
|
|
||||||
|
.temp
|
||||||
|
|
||||||
|
# Docusaurus cache and generated files
|
||||||
|
|
||||||
|
.docusaurus
|
||||||
|
|
||||||
|
# Serverless directories
|
||||||
|
|
||||||
|
.serverless/
|
||||||
|
|
||||||
|
# FuseBox cache
|
||||||
|
|
||||||
|
.fusebox/
|
||||||
|
|
||||||
|
# DynamoDB Local files
|
||||||
|
|
||||||
|
.dynamodb/
|
||||||
|
|
||||||
|
# TernJS port file
|
||||||
|
|
||||||
|
.tern-port
|
||||||
|
|
||||||
|
# Stores VSCode versions used for testing VSCode extensions
|
||||||
|
|
||||||
|
.vscode-test
|
||||||
|
|
||||||
|
# yarn v2
|
||||||
|
|
||||||
|
.yarn/cache
|
||||||
|
.yarn/unplugged
|
||||||
|
.yarn/build-state.yml
|
||||||
|
.yarn/install-state.gz
|
||||||
|
.pnp.*
|
||||||
|
|
||||||
|
# IntelliJ based IDEs
|
||||||
|
.idea
|
||||||
|
|
||||||
|
# Finder (MacOS) folder config
|
||||||
|
.DS_Store
|
13
README.md
Normal file
13
README.md
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
# (S)ophie's (S)erver (S)ite (G)enerator
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
`bun add git+https://git.sad.ovh/sophie/sssg`
|
||||||
|
|
||||||
|
|
||||||
|
## design goals:
|
||||||
|
1. rewrite and generate custom HTML/TS/JS/whatever..
|
||||||
|
2. allow for variables and other important features in buildsystem->html
|
||||||
|
3. plugins, this ties together the top two
|
||||||
|
4. HMR and HTML/CSS reloading (in the Dev plugin)
|
||||||
|
5. Rewriteable file renaming
|
||||||
|
6. Every plugin runs on the same set of files, and can rename and reinject multiple times
|
19
package.json
Normal file
19
package.json
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"name": "sssg",
|
||||||
|
"module": "index.ts",
|
||||||
|
"type": "module",
|
||||||
|
"main": "src/index.ts",
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/bun": "latest"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"typescript": "^5.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@types/mime-types": "^2.1.4",
|
||||||
|
"esbuild": "^0.23.0",
|
||||||
|
"marked": "^13.0.3",
|
||||||
|
"mime-types": "^2.1.35",
|
||||||
|
"preact": "^10.23.1"
|
||||||
|
}
|
||||||
|
}
|
106
src/index.ts
Normal file
106
src/index.ts
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
export abstract class Plugin {
|
||||||
|
abstract name: string;
|
||||||
|
abstract rewriteTriggers: string[];
|
||||||
|
renameTo?: string;
|
||||||
|
abstract longLasting: boolean;
|
||||||
|
abstract build?(): void;
|
||||||
|
|
||||||
|
abstract rewriteFile(
|
||||||
|
file: string,
|
||||||
|
filePath: string
|
||||||
|
): Promise<string | undefined | null | void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
import * as fs from "fs";
|
||||||
|
import * as path from "path";
|
||||||
|
|
||||||
|
export default class SSSG {
|
||||||
|
plugins: Plugin[] = [];
|
||||||
|
outputFolder!: string;
|
||||||
|
inputFolder!: string;
|
||||||
|
constructor({
|
||||||
|
outputFolder,
|
||||||
|
inputFolder,
|
||||||
|
}: {
|
||||||
|
outputFolder: string;
|
||||||
|
inputFolder: string;
|
||||||
|
}) {
|
||||||
|
this.inputFolder = inputFolder;
|
||||||
|
this.outputFolder = outputFolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
async run({ plugins }: { plugins: Plugin[] }) {
|
||||||
|
this.plugins = plugins;
|
||||||
|
if (!fs.existsSync(this.outputFolder)) fs.mkdirSync(this.outputFolder);
|
||||||
|
}
|
||||||
|
|
||||||
|
async build() {
|
||||||
|
console.log("build triggered")
|
||||||
|
for(const plugin of this.plugins) {
|
||||||
|
if(plugin.build) {
|
||||||
|
plugin.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
fs.rmSync(this.outputFolder, {recursive: true});
|
||||||
|
} catch {}
|
||||||
|
|
||||||
|
const sourceFiles = fs.readdirSync(this.inputFolder, {
|
||||||
|
recursive: true,
|
||||||
|
withFileTypes: true,
|
||||||
|
});
|
||||||
|
const globalPlugins = this.plugins.filter((z) =>
|
||||||
|
z.rewriteTriggers.includes("*")
|
||||||
|
);
|
||||||
|
|
||||||
|
for await (const file of sourceFiles) {
|
||||||
|
if (!file.isFile()) continue;
|
||||||
|
|
||||||
|
const type = file.name.split(".").at(-1);
|
||||||
|
if (!type) continue;
|
||||||
|
|
||||||
|
const shortname = file.name.slice(
|
||||||
|
0,
|
||||||
|
file.name.length - (type.length + 1)
|
||||||
|
);
|
||||||
|
const availablePlugins = this.plugins.filter((z) =>
|
||||||
|
z.rewriteTriggers.includes(type)
|
||||||
|
);
|
||||||
|
console.log(availablePlugins)
|
||||||
|
|
||||||
|
if (availablePlugins.length == 0) {
|
||||||
|
const oldPath = path.join(file.parentPath, file.name);
|
||||||
|
fs.cpSync(
|
||||||
|
oldPath,
|
||||||
|
oldPath.replace(this.inputFolder, this.outputFolder)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
for await (const plugin of availablePlugins) {
|
||||||
|
const oldPath = path.join(file.parentPath, file.name);
|
||||||
|
const newPath = path
|
||||||
|
.join(
|
||||||
|
file.parentPath,
|
||||||
|
shortname +
|
||||||
|
"." +
|
||||||
|
(plugin.rewriteTriggers.includes("*") ? type : plugin.renameTo)
|
||||||
|
)
|
||||||
|
.replace(this.inputFolder, this.outputFolder);
|
||||||
|
let data = fs.readFileSync(oldPath).toString("utf8");
|
||||||
|
|
||||||
|
for await (const globalPlugin of globalPlugins) {
|
||||||
|
const rewritten = await globalPlugin.rewriteFile(data, oldPath);
|
||||||
|
if (!rewritten) continue;
|
||||||
|
data = rewritten;
|
||||||
|
}
|
||||||
|
|
||||||
|
let rewrite = await plugin.rewriteFile(data, oldPath);
|
||||||
|
|
||||||
|
if (!rewrite) continue;
|
||||||
|
|
||||||
|
fs.mkdirSync(path.dirname(newPath), { recursive: true });
|
||||||
|
fs.writeFileSync(newPath, rewrite);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
98
src/plugins/dev.ts
Normal file
98
src/plugins/dev.ts
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
import type { Server, ServerWebSocket } from "bun";
|
||||||
|
import SSSG, { Plugin } from "..";
|
||||||
|
import * as fs from "fs";
|
||||||
|
import mime from "mime-types";
|
||||||
|
|
||||||
|
const script = `let reconnectTimeout;function connect(){console.log("[--dev] connecting to dev server");let ws=new WebSocket("ws://localhost:8080");ws.addEventListener("message",message=>{if(message.data=="refresh"){location.reload()}});ws.addEventListener("open",()=>{console.log("[--dev] connected")});ws.addEventListener("close",()=>{console.log("[--dev] socket closed, restarting in 1s");clearTimeout(reconnectTimeout);reconnectTimeout=setTimeout(()=>{connect()},1e3)})}window.addEventListener("load",()=>connect());`;
|
||||||
|
|
||||||
|
export default class DevPlugin extends Plugin {
|
||||||
|
build: undefined;
|
||||||
|
|
||||||
|
name = "dev";
|
||||||
|
rewriteTriggers = [];
|
||||||
|
renameTo = undefined;
|
||||||
|
longLasting = true;
|
||||||
|
server!: Server;
|
||||||
|
allConnections: ServerWebSocket<number>[] = [];
|
||||||
|
|
||||||
|
constructor(sssg: SSSG) {
|
||||||
|
super();
|
||||||
|
if (!process.argv.includes("--dev")) return;
|
||||||
|
|
||||||
|
fs.watch(
|
||||||
|
sssg.inputFolder,
|
||||||
|
{
|
||||||
|
recursive: true,
|
||||||
|
},
|
||||||
|
async (e, f) => {
|
||||||
|
console.log("[dev] Noticed update in " + f + ", of type " + e + ".");
|
||||||
|
this.allConnections.forEach((z) => z.send("refresh"));
|
||||||
|
await sssg.build();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
this.server = Bun.serve<number>({
|
||||||
|
fetch(req, server) {
|
||||||
|
const success = server.upgrade(req);
|
||||||
|
if (success) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = new URL(req.url);
|
||||||
|
|
||||||
|
let cleanedPath = url.pathname;
|
||||||
|
if (cleanedPath == "/") cleanedPath = "/index.html";
|
||||||
|
if (cleanedPath.endsWith("/")) cleanedPath = cleanedPath.slice(0, -1);
|
||||||
|
|
||||||
|
let fsPath = sssg.outputFolder + cleanedPath;
|
||||||
|
|
||||||
|
if (fsPath.match(/\.\.\//g) !== null) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
let rawFile;
|
||||||
|
try {
|
||||||
|
rawFile = fs.readFileSync(fsPath);
|
||||||
|
} catch {
|
||||||
|
return new Response("404 Not Found", {
|
||||||
|
status: 404,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const type = fsPath.split(".").at(-1);
|
||||||
|
if (!type) return;
|
||||||
|
if (type == "html") {
|
||||||
|
|
||||||
|
rawFile = rawFile.toString().replace(
|
||||||
|
"<head>",
|
||||||
|
`<head><script>${script}</script>`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return new Response(rawFile, {
|
||||||
|
headers: {
|
||||||
|
"Cache-Control": "no-cache, no-store, must-revalidate",
|
||||||
|
"Pragma": "no-cache",
|
||||||
|
"Expires": "0",
|
||||||
|
"Content-Type": (mime.lookup(type) || "application/octet-stream") + "; charset=utf-8",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
websocket: {
|
||||||
|
open: (ws) => {
|
||||||
|
ws.data = Math.random();
|
||||||
|
|
||||||
|
this.allConnections.push(ws);
|
||||||
|
},
|
||||||
|
message(ws, message) {},
|
||||||
|
close: (ws) => {
|
||||||
|
this.allConnections = this.allConnections.filter(
|
||||||
|
(z) => z.data != ws.data
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
port: 8080,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async rewriteFile(file: string, filePath: string) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
24
src/plugins/markdown-compiler.ts
Normal file
24
src/plugins/markdown-compiler.ts
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import { Plugin } from "..";
|
||||||
|
import { marked } from "marked";
|
||||||
|
import { parseMetadata } from "./markdown-metadata";
|
||||||
|
|
||||||
|
export default class MarkdownCompiler extends Plugin {
|
||||||
|
build: undefined;
|
||||||
|
|
||||||
|
name = "markdown-compiler";
|
||||||
|
rewriteTriggers = ["md"]
|
||||||
|
renameTo = "html"
|
||||||
|
longLasting = false;
|
||||||
|
|
||||||
|
async rewriteFile(file: string, filePath: string) {
|
||||||
|
let text = file;
|
||||||
|
const metadata = parseMetadata(text);
|
||||||
|
if(metadata) {
|
||||||
|
let textSplit = text.split('\n');
|
||||||
|
textSplit.splice(0, Object.keys(metadata).length);
|
||||||
|
textSplit.unshift(metadata.title)
|
||||||
|
text = textSplit.join("\n");
|
||||||
|
}
|
||||||
|
return await marked.parse(text);
|
||||||
|
}
|
||||||
|
}
|
31
src/plugins/markdown-metadata.ts
Normal file
31
src/plugins/markdown-metadata.ts
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
import { Plugin } from "..";
|
||||||
|
|
||||||
|
export function parseMetadata(file: string) {
|
||||||
|
if (!/^=+$/gm.test(file)) return;
|
||||||
|
const splitfile = file.split("\n");
|
||||||
|
let properties: Record<string, string> | undefined;
|
||||||
|
for (let i = 0; i < splitfile.length; i++) {
|
||||||
|
if (!properties) properties = {};
|
||||||
|
const line = splitfile[i];
|
||||||
|
if (/^=+$/gm.test(line)) break;
|
||||||
|
const parts = line.split("=");
|
||||||
|
if (parts.length !== 2) break;
|
||||||
|
properties[parts[0].trim()] = parts[1].trim();
|
||||||
|
}
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class MarkdownMetadataGenerator extends Plugin {
|
||||||
|
build: undefined;
|
||||||
|
|
||||||
|
name = "markdown-metadata";
|
||||||
|
rewriteTriggers = ["md"];
|
||||||
|
renameTo = "json";
|
||||||
|
longLasting = false;
|
||||||
|
|
||||||
|
async rewriteFile(file: string, filePath: string) {
|
||||||
|
const metadata = parseMetadata(file);
|
||||||
|
if (!metadata) return;
|
||||||
|
return JSON.stringify(metadata);
|
||||||
|
}
|
||||||
|
}
|
58
src/plugins/ts-compiler.ts
Normal file
58
src/plugins/ts-compiler.ts
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
import { Plugin } from "..";
|
||||||
|
import * as fs from "fs";
|
||||||
|
import * as esbuild from "esbuild";
|
||||||
|
|
||||||
|
export default class TSCompiler extends Plugin {
|
||||||
|
build: undefined;
|
||||||
|
|
||||||
|
name = "ts-compiler";
|
||||||
|
rewriteTriggers = ["ts", "tsx", "jsx"];
|
||||||
|
renameTo = "js";
|
||||||
|
longLasting = false;
|
||||||
|
|
||||||
|
minify = false;
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
if (process.argv.includes("--prod")) {
|
||||||
|
this.minify = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async rewriteFile(file: string, filePath: string) {
|
||||||
|
let result;
|
||||||
|
try {
|
||||||
|
result = await esbuild.build({
|
||||||
|
stdin: {
|
||||||
|
contents: file,
|
||||||
|
resolveDir: filePath.split("/")?.slice(0, -1).join("/"),
|
||||||
|
sourcefile: filePath.split("/").at(-1),
|
||||||
|
loader: filePath.split("/").at(-1)?.split(".").at(-1) as
|
||||||
|
| "ts"
|
||||||
|
| "tsx"
|
||||||
|
| "jsx",
|
||||||
|
},
|
||||||
|
jsxFragment: "Fragment",
|
||||||
|
jsxFactory: "h",
|
||||||
|
jsxImportSource: "preact",
|
||||||
|
jsx: "transform",
|
||||||
|
write: false,
|
||||||
|
bundle: true,
|
||||||
|
outdir: "out",
|
||||||
|
minify: this.minify,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
console.log("Errored!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.errors.length != 0) {
|
||||||
|
console.log("TS compiler errored.");
|
||||||
|
result.errors.forEach((element) => {
|
||||||
|
console.error(element);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const output = result.outputFiles[0].contents;
|
||||||
|
return new TextDecoder().decode(output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
29
src/plugins/variables.ts
Normal file
29
src/plugins/variables.ts
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import { Plugin } from "..";
|
||||||
|
|
||||||
|
export default class Variables extends Plugin {
|
||||||
|
|
||||||
|
name = "variables";
|
||||||
|
rewriteTriggers = ["html", "*"];
|
||||||
|
renameTo = undefined;
|
||||||
|
longLasting = false;
|
||||||
|
|
||||||
|
varBuild!: () => Record<string, string>;
|
||||||
|
variables!: Record<string, string>;
|
||||||
|
|
||||||
|
constructor(varBuild: () => Record<string, string>) {
|
||||||
|
super();
|
||||||
|
this.varBuild = varBuild;
|
||||||
|
}
|
||||||
|
|
||||||
|
build() {
|
||||||
|
this.variables = this.varBuild();
|
||||||
|
}
|
||||||
|
|
||||||
|
async rewriteFile(file: string, filePath: string): Promise<string> {
|
||||||
|
let prevfile = file;
|
||||||
|
for (const a of Object.entries(this.variables)) {
|
||||||
|
prevfile = prevfile.replaceAll(a[0], a[1]);
|
||||||
|
}
|
||||||
|
return prevfile;
|
||||||
|
}
|
||||||
|
}
|
27
tsconfig.json
Normal file
27
tsconfig.json
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
// Enable latest features
|
||||||
|
"lib": ["ESNext", "DOM"],
|
||||||
|
"target": "ESNext",
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleDetection": "force",
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"allowJs": true,
|
||||||
|
|
||||||
|
// Bundler mode
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"verbatimModuleSyntax": true,
|
||||||
|
"noEmit": true,
|
||||||
|
|
||||||
|
// Best practices
|
||||||
|
"strict": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
|
||||||
|
// Some stricter flags (disabled by default)
|
||||||
|
"noUnusedLocals": false,
|
||||||
|
"noUnusedParameters": false,
|
||||||
|
"noPropertyAccessFromIndexSignature": false
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue