Scenario generator for vore roleplay and story ideas.
https://scenario-generator.deliciousreya.net/responses
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
208 lines
7.4 KiB
208 lines
7.4 KiB
import {
|
|
createPrinter,
|
|
parseJsonText,
|
|
factory,
|
|
NewLineKind,
|
|
NodeFlags,
|
|
type PropertyAssignment,
|
|
SyntaxKind,
|
|
type VariableDeclaration,
|
|
type VariableStatement
|
|
} from 'typescript';
|
|
import typescriptModule from 'typescript';
|
|
import { readFile, writeFile, readdir } from 'node:fs/promises';
|
|
import { basename, dirname, join, normalize } from 'node:path';
|
|
import {createHash} from 'node:crypto';
|
|
import {camelCase} from 'change-case';
|
|
import { render as renderLess } from 'less';
|
|
import CleanCSS from 'clean-css';
|
|
import type {
|
|
HashedBundled,
|
|
SourceMappedHashedBundled,
|
|
SourceMappedBundled,
|
|
Bundled,
|
|
MaybeSourceMappedHashedBundled, SourceMap
|
|
} from '../common/bundle';
|
|
import { rollup, type RollupCache } from 'rollup';
|
|
import typescript from 'rollup-plugin-ts';
|
|
import terser from '@rollup/plugin-terser';
|
|
import nodeResolve from '@rollup/plugin-node-resolve';
|
|
import commonJs from '@rollup/plugin-commonjs';
|
|
|
|
function* assignProperties(pairs: Iterable<[string, MaybeSourceMappedHashedBundled]>, includeSourceMap: boolean): Generator<PropertyAssignment> {
|
|
for (const [identifier, { bundled, hash, sourceMap }] of pairs) {
|
|
yield factory.createPropertyAssignment(
|
|
factory.createIdentifier(identifier),
|
|
factory.createObjectLiteralExpression([
|
|
factory.createPropertyAssignment(
|
|
factory.createIdentifier("bundled"),
|
|
factory.createNoSubstitutionTemplateLiteral(bundled)
|
|
),
|
|
factory.createPropertyAssignment(
|
|
factory.createIdentifier("hash"),
|
|
factory.createStringLiteral(hash)
|
|
),
|
|
...(includeSourceMap && sourceMap ? [factory.createPropertyAssignment(
|
|
factory.createIdentifier("sourceMap"),
|
|
parseJsonText(hash + ".map", JSON.stringify(sourceMap)).statements[0].expression,
|
|
)] : [])
|
|
], true));
|
|
}
|
|
}
|
|
|
|
function declareObjectLiteral(identifier: string, pairs: Iterable<[string, MaybeSourceMappedHashedBundled]>, includeSourceMap: boolean): VariableDeclaration {
|
|
return factory.createVariableDeclaration(
|
|
factory.createIdentifier(identifier),
|
|
undefined,
|
|
undefined,
|
|
factory.createSatisfiesExpression(
|
|
factory.createAsExpression(
|
|
factory.createObjectLiteralExpression(Array.from(assignProperties(pairs, includeSourceMap)), true),
|
|
factory.createTypeReferenceNode(factory.createIdentifier('const'))),
|
|
factory.createTypeReferenceNode(
|
|
factory.createIdentifier("Record"),
|
|
[
|
|
factory.createTypeReferenceNode(factory.createIdentifier("string")),
|
|
factory.createTypeReferenceNode(factory.createIdentifier("MaybeSourceMappedHashedBundled"))])));
|
|
}
|
|
|
|
function exportObjectLiteral(identifier: string, pairs: Iterable<[string, MaybeSourceMappedHashedBundled]>, includeSourceMap: boolean): VariableStatement {
|
|
return factory.createVariableStatement(
|
|
[factory.createToken(SyntaxKind.ExportKeyword)],
|
|
factory.createVariableDeclarationList([declareObjectLiteral(identifier, pairs, includeSourceMap)], NodeFlags.Const)
|
|
);
|
|
}
|
|
|
|
async function processLess(atPath: string): Promise<SourceMappedBundled> {
|
|
const fileBase = basename(atPath.substring(0, atPath.length - LESS_SUFFIX.length));
|
|
const { css: lessCss, map: lessMap } = await renderLess(await readFile(atPath, { encoding: 'utf-8' }), {
|
|
paths: [dirname(atPath)],
|
|
math: 'strict',
|
|
strictUnits: true,
|
|
filename: fileBase + '.less',
|
|
strictImports: true,
|
|
sourceMap: {
|
|
outputSourceFiles: true,
|
|
}
|
|
});
|
|
const { styles, sourceMap } = await new CleanCSS({
|
|
sourceMap: true,
|
|
sourceMapInlineSources: true,
|
|
returnPromise: true,
|
|
level: 2,
|
|
format: false,
|
|
inline: ['all'],
|
|
rebase: false,
|
|
compatibility: '*',
|
|
fetch(uri): never {
|
|
throw Error(`external files are unexpected after less compilation, but found ${uri}`)
|
|
},
|
|
}).minify({
|
|
[fileBase + '.css']: {
|
|
styles: lessCss,
|
|
sourceMap: lessMap
|
|
}
|
|
})
|
|
return { bundled: styles, sourceMap: {...JSON.parse(sourceMap!.toString()), file: fileBase + ".css"} as SourceMap };
|
|
}
|
|
|
|
async function processTypescript(atPath: string, inDir: string, cache?: RollupCache): Promise<{cache: RollupCache, bundle: SourceMappedBundled}> {
|
|
const build = await rollup({
|
|
cache: cache ?? true,
|
|
input: atPath,
|
|
plugins: [
|
|
nodeResolve({
|
|
}),
|
|
commonJs({
|
|
}),
|
|
typescript({
|
|
transpiler: "babel",
|
|
typescript: typescriptModule,
|
|
tsconfig: join(inDir, 'tsconfig.json')
|
|
}),
|
|
terser({})
|
|
]
|
|
})
|
|
const {output: [chunk]} = await build.generate({
|
|
name: camelCase(basename(atPath.substring(0, atPath.length - TS_SUFFIX.length))),
|
|
sourcemap: 'hidden',
|
|
sourcemapFile: join(inDir, 'sourcemap.map'),
|
|
format: 'iife',
|
|
compact: true,
|
|
})
|
|
return {
|
|
cache: build.cache!,
|
|
bundle: {
|
|
bundled: chunk.code,
|
|
sourceMap: chunk.map!
|
|
}
|
|
}
|
|
}
|
|
|
|
const LESS_SUFFIX = '-entrypoint.less';
|
|
const TS_SUFFIX = '-entrypoint.ts';
|
|
|
|
function hashBundled<T extends Bundled>(value: T & {readonly hash?: never}): T & HashedBundled {
|
|
const hash = createHash('sha256').update(value.bundled).digest('hex')
|
|
return {
|
|
...value,
|
|
hash,
|
|
}
|
|
}
|
|
|
|
export async function getBundle(inDir: string): Promise<{ css: Map<string, SourceMappedHashedBundled>, js: Map<string, SourceMappedHashedBundled> }> {
|
|
const css = new Map<string, SourceMappedHashedBundled>();
|
|
const js = new Map<string, SourceMappedHashedBundled>();
|
|
const dir = await readdir(inDir, { withFileTypes: true });
|
|
let cache: RollupCache|undefined = undefined
|
|
for (const ent of dir) {
|
|
if (!ent.isFile()) {
|
|
continue;
|
|
}
|
|
if (ent.name.endsWith(LESS_SUFFIX)) {
|
|
css.set(camelCase(ent.name.substring(0, ent.name.length - LESS_SUFFIX.length)), hashBundled(await processLess(join(inDir, ent.name))));
|
|
} else if (ent.name.endsWith(TS_SUFFIX)) {
|
|
const {cache: newCache, bundle} = await processTypescript(join(inDir, ent.name), inDir, cache)
|
|
cache = newCache
|
|
js.set(camelCase(ent.name.substring(0, ent.name.length - TS_SUFFIX.length)), hashBundled(bundle));
|
|
} else {
|
|
// continue;
|
|
}
|
|
}
|
|
return { css, js };
|
|
}
|
|
|
|
export const DEFAULT_IN_PATH = normalize(join(__dirname, '../../src/client/'))
|
|
export const DEFAULT_OUT_PATH = normalize(join(__dirname, '../../src/server/web/bundles/client.generated.ts'))
|
|
|
|
export async function writeBundle({ css, js }: {css: Map<string, SourceMappedHashedBundled>, js: Map<string, SourceMappedHashedBundled>}, outFile: string, includeSourceMap: true): Promise<void>
|
|
export async function writeBundle({ css, js }: {css: Map<string, HashedBundled>, js: Map<string, HashedBundled>}, outFile: string, includeSourceMap: false): Promise<void>
|
|
export async function writeBundle({ css, js }: {css: Map<string, MaybeSourceMappedHashedBundled>, js: Map<string, MaybeSourceMappedHashedBundled>}, outFile: string, includeSourceMap: boolean): Promise<void>
|
|
export async function writeBundle({ css, js }: {css: Map<string, MaybeSourceMappedHashedBundled>, js: Map<string, MaybeSourceMappedHashedBundled>}, outFile: string, includeSourceMap: boolean): Promise<void> {
|
|
const printer = createPrinter({
|
|
newLine: NewLineKind.LineFeed,
|
|
omitTrailingSemicolon: true
|
|
});
|
|
await writeFile(outFile, printer.printFile(factory.createSourceFile([
|
|
factory.createImportDeclaration(
|
|
undefined,
|
|
factory.createImportClause(
|
|
false,
|
|
undefined,
|
|
factory.createNamedImports([
|
|
factory.createImportSpecifier(
|
|
true,
|
|
undefined,
|
|
factory.createIdentifier( "MaybeSourceMappedHashedBundled")),
|
|
])
|
|
),
|
|
factory.createStringLiteral("../../common/bundle.js")),
|
|
exportObjectLiteral('CSS', css, includeSourceMap),
|
|
exportObjectLiteral('JS', js, includeSourceMap)
|
|
], factory.createToken(SyntaxKind.EndOfFileToken), NodeFlags.None)), {
|
|
encoding: 'utf-8',
|
|
mode: 0o644
|
|
});
|
|
}
|
|
|
|
|
|
|