@hellpig/anarchy-shared 1.5.2 → 1.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,3 +4,4 @@
4
4
  - 1.3.0 Vite plugin: emit json
5
5
  - 1.4.0 Json validation utils
6
6
  - 1.5.0 Added proper building and npm packaging
7
+ - 1.6.0 Fixes npm package
package/README.md CHANGED
@@ -31,4 +31,4 @@ SOFTWARE.
31
31
  See local files `LICENSE`, `CHANGELOG`, `NOTICE` (pointer), and files in `./legal`:
32
32
  `DISCLAIMER`, `EULA`, `PRIVACY`, `SECURITY`, `NOTICE` (full), `THIRD_PARTY_LICENSES`.
33
33
 
34
- Contacts — Privacy: {{PRIVACY_EMAIL}}, Security: {{SECURITY_EMAIL}}.
34
+ Contacts — Privacy: pnf036+anarchy@gmail.com, Security: pnf036+anarchy_security@gmail.com.
@@ -0,0 +1,74 @@
1
+ #!/bin/bash
2
+
3
+ JS_FILE="$1"
4
+ MAP_FILE="$2"
5
+ GZ_FILE="${JS_FILE}.gz"
6
+ BR_FILE="${JS_FILE}.br"
7
+
8
+ if [[ -z "$JS_FILE" || -z "$MAP_FILE" ]]; then
9
+ echo "❌ Usage: ./CheckMinify.sh path/to/file.js path/to/file.js.map"
10
+ exit 1
11
+ fi
12
+
13
+ if [[ ! -f "$JS_FILE" ]]; then
14
+ echo "❌ JS file not found: $JS_FILE"
15
+ exit 1
16
+ fi
17
+
18
+ if [[ ! -f "$MAP_FILE" ]]; then
19
+ echo "❌ Map file not found: $MAP_FILE"
20
+ exit 1
21
+ fi
22
+
23
+ #LINES=$(wc -l < "$JS_FILE")
24
+ #if [[ "$LINES" -gt 50 ]]; then
25
+ # echo "❌ Too many lines in JS file ($LINES). Probably not minified."
26
+ # exit 1
27
+ #else
28
+ # echo "✅ JS file appears minified ($LINES lines)"
29
+ #fi
30
+
31
+ if [[ -f "$GZ_FILE" ]]; then
32
+ gunzip -c "$GZ_FILE" | cmp -s - "$JS_FILE"
33
+ if [[ $? -ne 0 ]]; then
34
+ echo "❌ Gzip file doesn't match JS file"
35
+ exit 1
36
+ else
37
+ echo "✅ Gzip file matches"
38
+ fi
39
+ else
40
+ echo "⚠️ Gzip file not found: $GZ_FILE"
41
+ fi
42
+
43
+ if [[ -f "$BR_FILE" ]]; then
44
+ brotli -d -c "$BR_FILE" | cmp -s - "$JS_FILE"
45
+ if [[ $? -ne 0 ]]; then
46
+ echo "❌ Brotli file doesn't match JS file"
47
+ exit 1
48
+ else
49
+ echo "✅ Brotli file matches"
50
+ fi
51
+ else
52
+ echo "⚠️ Brotli file not found: $BR_FILE"
53
+ fi
54
+
55
+ echo "🔍 Checking sourcemap integrity..."
56
+
57
+ TMP_JSON=$(mktemp -t sourcemapXXXXXX.json)
58
+
59
+ SOURCEMAP_ERR=$(npx --yes source-map-explorer "$JS_FILE" "$MAP_FILE" --json "$TMP_JSON" 2>&1)
60
+ echo "📄 Sourcemap analysis written to: $TMP_JSON"
61
+
62
+ if [[ $? -eq 0 && -s "$TMP_JSON" ]]; then
63
+ echo "✅ Sourcemap check passed: $JS_FILE"
64
+ rm "$TMP_JSON"
65
+ else
66
+ echo "❌ Failed to analyze sourcemap: $JS_FILE"
67
+ echo "🧾 stderr:"
68
+ echo "$SOURCEMAP_ERR"
69
+ [[ -f "$TMP_JSON" ]] && cat "$TMP_JSON"
70
+ rm -f "$TMP_JSON"
71
+ exit 1
72
+ fi
73
+
74
+ echo "🎉 All checks passed"
@@ -0,0 +1,24 @@
1
+ import { copy } from 'fs-extra';
2
+ import { resolve } from 'path';
3
+
4
+ const args = process.argv.slice(2);
5
+ const fromArg = args.find((arg) => arg.startsWith('from='));
6
+ const toArg = args.find((arg) => arg.startsWith('to='));
7
+
8
+ const fromPath = resolve(process.cwd(), fromArg.split('=')[1]);
9
+ const toPath = resolve(process.cwd(), toArg.split('=')[1]);
10
+
11
+ console.log(`Copying files...\nFrom: ${fromPath}\nTo: ${toPath}`);
12
+
13
+ try {
14
+ await copy(fromPath, toPath, {
15
+ overwrite: true,
16
+ errorOnExist: false,
17
+ recursive: true
18
+ });
19
+
20
+ console.log('✅ Copy completed.');
21
+ } catch (err) {
22
+ console.error('Copy failed:', err);
23
+ process.exit(1);
24
+ }
@@ -0,0 +1,40 @@
1
+ import { copyFile, mkdir } from 'fs/promises';
2
+ import { resolve, dirname } from 'path';
3
+
4
+ const args = process.argv.slice(2);
5
+
6
+ // last arg must be the destination (e.g. to=dist-app/draco)
7
+ const toArg = args.find((arg) => arg.startsWith('to='));
8
+ if (!toArg) {
9
+ console.error('❌ Missing argument: to=<destination folder>');
10
+ process.exit(1);
11
+ }
12
+
13
+ const toDir = resolve(process.cwd(), toArg.split('=')[1]);
14
+
15
+ // all other args treated as relative file paths to copy
16
+ const filePaths = args.filter((arg) => !arg.startsWith('to='));
17
+ if (filePaths.length === 0) {
18
+ console.error('❌ No files to copy provided.');
19
+ process.exit(1);
20
+ }
21
+
22
+ console.log(`Copying files to: ${toDir}`);
23
+ console.log(filePaths.map((f) => ` - ${f}`).join('\n'));
24
+
25
+ (async () => {
26
+ try {
27
+ for (const relativePath of filePaths) {
28
+ const src = resolve(process.cwd(), relativePath);
29
+ const dest = resolve(toDir, relativePath.split('/').pop());
30
+
31
+ await mkdir(dirname(dest), { recursive: true });
32
+ await copyFile(src, dest);
33
+ }
34
+
35
+ console.log('✅ Files copied successfully.');
36
+ } catch (err) {
37
+ console.error('❌ Copy failed:', err);
38
+ process.exit(1);
39
+ }
40
+ })();
@@ -0,0 +1,55 @@
1
+ import path from 'node:path';
2
+ import fs from 'node:fs';
3
+ import { config as dotenvConfig } from 'dotenv';
4
+ import { expand as dotenvExpand } from 'dotenv-expand';
5
+
6
+ export function buildEnvChain(mode) {
7
+ const chain = ['.env', '.env.local'];
8
+ const tokens = String(mode).split('.').filter(Boolean);
9
+ const first = tokens[0]?.toLowerCase();
10
+ const firstAlias = first === 'dev' ? 'development' : first === 'prod' ? 'production' : undefined;
11
+
12
+ const seqs = [tokens];
13
+ if (firstAlias && firstAlias !== first) seqs.push([firstAlias, ...tokens.slice(1)]);
14
+
15
+ for (const seq of seqs) {
16
+ const accum = [];
17
+ for (const p of seq) {
18
+ accum.push(p);
19
+ const key = accum.join('.');
20
+ chain.push(`.env.${key}`);
21
+ chain.push(`.env.${key}.local`);
22
+ }
23
+ }
24
+
25
+ return Array.from(new Set(chain));
26
+ }
27
+
28
+ export function loadModeEnv(mode, cwd = process.cwd()) {
29
+ const loadEnvFile = (file) => {
30
+ const full = path.resolve(cwd, file);
31
+ if (!fs.existsSync(full)) return false;
32
+ const result = dotenvConfig({ path: full, override: true });
33
+ if (result.parsed) dotenvExpand({ parsed: result.parsed });
34
+ return true;
35
+ };
36
+
37
+ const chain = buildEnvChain(mode);
38
+ for (const file of chain) loadEnvFile(file);
39
+ }
40
+
41
+ export function parseListEnv(val) {
42
+ if (!val) return [];
43
+ return String(val)
44
+ .split(',')
45
+ .map((s) => s.trim())
46
+ .filter(Boolean);
47
+ }
48
+
49
+ export function parseBoolEnv(val, defaultValue = false) {
50
+ if (val == null) return defaultValue;
51
+ const s = String(val).trim().toLowerCase();
52
+ if (s === '1' || s === 'true' || s === 'yes' || s === 'on') return true;
53
+ if (s === '0' || s === 'false' || s === 'no' || s === 'off') return false;
54
+ return defaultValue;
55
+ }
@@ -0,0 +1,43 @@
1
+ import { writeFileSync, existsSync, readFileSync } from 'fs';
2
+ import { resolve, dirname } from 'path';
3
+ import { fileURLToPath } from 'url';
4
+
5
+ const cwd = process.cwd();
6
+
7
+ const args = process.argv.slice(2);
8
+ const pkgArg = args.find((arg) => arg.startsWith('--pkg='));
9
+ const outArg = args.find((arg) => arg.startsWith('--out='));
10
+
11
+ const pkgPath = pkgArg ? resolve(cwd, pkgArg.split('=')[1]) : resolve(cwd, 'package.json');
12
+
13
+ const outPath = outArg ? resolve(cwd, outArg.split('=')[1]) : resolve(cwd, './versions.ts');
14
+
15
+ if (!existsSync(pkgPath)) {
16
+ console.error(`❌ package.json not found at: ${pkgPath}`);
17
+ process.exit(1);
18
+ }
19
+
20
+ const raw = readFileSync(pkgPath, 'utf8');
21
+ let version;
22
+
23
+ try {
24
+ const pkg = JSON.parse(raw);
25
+ version = pkg.version;
26
+ } catch (err) {
27
+ console.error(`❌ Failed to parse package.json:`, err);
28
+ process.exit(1);
29
+ }
30
+
31
+ if (!version) {
32
+ console.error(`❌ "version" field not found in ${pkgPath}`);
33
+ process.exit(1);
34
+ }
35
+
36
+ const content = `// This file is auto-generated by GenerateVersionsFile.js
37
+ // Do not edit manually. Run the script to update.
38
+
39
+ export const packageJsonVersion = '${version}';\n`;
40
+ writeFileSync(outPath, content, 'utf8');
41
+
42
+ console.log(`✅ version.ts generated at: ${outPath}`);
43
+ console.log(`📦 version: ${version}`);
@@ -0,0 +1,37 @@
1
+ import { Buffer } from 'buffer';
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+ import encode from 'png-chunks-encode';
5
+ import extract from 'png-chunks-extract';
6
+
7
+ //THis script meant to be used for testing of cleaning metadata
8
+
9
+ const inputPath = path.resolve(process.cwd(), './test.png');
10
+ const outputPath = path.resolve(process.cwd(), './test.png');
11
+
12
+ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
13
+ export function createTextChunk(keyword, text) {
14
+ // eslint-disable-next-line spellcheck/spell-checker
15
+ const keywordBuffer = Buffer.from(keyword, 'latin1');
16
+ const nullByte = Buffer.from([0x00]);
17
+ // eslint-disable-next-line spellcheck/spell-checker
18
+ const textBuffer = Buffer.from(text, 'latin1');
19
+ const data = Buffer.concat([keywordBuffer, nullByte, textBuffer]);
20
+ return {
21
+ name: 'tEXt',
22
+ data
23
+ };
24
+ }
25
+
26
+ const pngBuffer = fs.readFileSync(inputPath);
27
+
28
+ const chunks = extract(pngBuffer);
29
+
30
+ const myTextChunk = createTextChunk('Author', 'Someone (to remove)');
31
+
32
+ chunks.splice(1, 0, myTextChunk);
33
+
34
+ const newBuffer = Buffer.from(encode(chunks));
35
+ fs.writeFileSync(outputPath, newBuffer);
36
+
37
+ console.log('✅ PNG with metadata saved to with-meta.png');
@@ -0,0 +1,139 @@
1
+ // Creates .ico file from one or more PNGs, with optional resizing.
2
+ // Usage:
3
+ // node scripts/MakeIcoFromPng.js --in assets/icon-1024.png --out assets/icon.ico
4
+ // node scripts/MakeIcoFromPng.js --in assets/256.png,assets/128.png --out assets/app.ico
5
+ // node scripts/MakeIcoFromPng.js --in assets/pngs --out assets/icon.ico
6
+ // node scripts/MakeIcoFromPng.js --in assets/icon-1024.png --out assets/icon.ico --size 256
7
+ //
8
+ // Notes:
9
+ // - If --size is provided, all inputs are resized to NxN (requires dev-dep 'sharp').
10
+ // - If --size is omitted, inputs are used as-is (no resizing).
11
+ // - png-to-ico builds a valid ICO from given PNG buffers.
12
+
13
+ import { readdir, readFile, stat, writeFile } from 'node:fs/promises';
14
+ import { extname, resolve } from 'node:path';
15
+ import process from 'node:process';
16
+ import pngToIco from 'png-to-ico';
17
+ import sharp from 'sharp';
18
+
19
+ function printHelp() {
20
+ console.log(
21
+ `mk-ico — build a .ico from PNG(s)
22
+
23
+ Options:
24
+ --in, -i Path to a PNG file. Can be repeated or comma-separated.
25
+ If a directory is passed, all *.png files in it are used (non-recursive).
26
+ --out, -o Output .ico path (required).
27
+ --size, -s Optional square size in px (e.g. 256). If omitted, images are not resized.
28
+ --help, -h Show this help.
29
+
30
+ Examples:
31
+ node scripts/MakeIcoFromPng.js --in assets/icon-1024.png --out assets/icon.ico
32
+ node scripts/MakeIcoFromPng.js -i assets/256.png,assets/128.png -o assets/icon.ico
33
+ node scripts/MakeIcoFromPng.js -i assets/pngs -o assets/icon.ico
34
+ node scripts/MakeIcoFromPng.js -i assets/icon-1024.png -o assets/icon.ico -s 256
35
+ `
36
+ );
37
+ }
38
+
39
+ function parseArgs(argv) {
40
+ const args = { inputs: [], out: null, help: false, size: null };
41
+ for (let i = 0; i < argv.length; i++) {
42
+ const a = argv[i];
43
+ if (a === '--help' || a === '-h') {
44
+ args.help = true;
45
+ continue;
46
+ }
47
+ if (a === '--out' || a === '-o') {
48
+ args.out = argv[++i];
49
+ continue;
50
+ }
51
+ if (a === '--in' || a === '-i') {
52
+ const val = argv[++i];
53
+ if (val) args.inputs.push(...val.split(','));
54
+ continue;
55
+ }
56
+ if (a === '--size' || a === '-s') {
57
+ const val = argv[++i];
58
+ if (val && /^\d+$/.test(val)) args.size = Number(val);
59
+ else throw new Error(`--size expects a positive integer, got: ${val ?? '(missing)'}`);
60
+ continue;
61
+ }
62
+ }
63
+ return args;
64
+ }
65
+
66
+ async function listPngsFromDir(dirPath) {
67
+ const entries = await readdir(dirPath, { withFileTypes: true });
68
+ return entries.filter((e) => e.isFile() && extname(e.name).toLowerCase() === '.png').map((e) => resolve(dirPath, e.name));
69
+ }
70
+
71
+ async function resolveInputs(maybePaths) {
72
+ const result = [];
73
+ for (const p of maybePaths) {
74
+ const abs = resolve(p);
75
+ let st;
76
+ try {
77
+ st = await stat(abs);
78
+ } catch {
79
+ throw new Error(`Input path does not exist: ${abs}`);
80
+ }
81
+
82
+ if (st.isDirectory()) {
83
+ const pngs = await listPngsFromDir(abs);
84
+ if (pngs.length === 0) throw new Error(`Directory has no PNGs: ${abs}`);
85
+ result.push(...pngs);
86
+ } else if (st.isFile()) {
87
+ if (extname(abs).toLowerCase() !== '.png') throw new Error(`Input is not a .png file: ${abs}`);
88
+ result.push(abs);
89
+ } else {
90
+ throw new Error(`Unsupported input type (not file/dir): ${abs}`);
91
+ }
92
+ }
93
+ // de-dup, keep order
94
+ return [...new Set(result)];
95
+ }
96
+
97
+ async function maybeResize(buf, size) {
98
+ if (!size) return buf;
99
+ if (!(Number.isInteger(size) && size > 0 && size <= 4096)) {
100
+ throw new Error(`--size must be an integer in range 1..4096, got ${size}`);
101
+ }
102
+ return sharp(buf).resize(size, size, { fit: 'contain', withoutEnlargement: false }).png().toBuffer();
103
+ }
104
+
105
+ async function main() {
106
+ const { inputs, out, help, size } = parseArgs(process.argv.slice(2));
107
+ if (help || inputs.length === 0 || !out) {
108
+ printHelp();
109
+ if (help) return;
110
+ process.exit(1);
111
+ }
112
+
113
+ const inFiles = await resolveInputs(inputs);
114
+ if (inFiles.length === 0) throw new Error('No PNG inputs resolved.');
115
+
116
+ // Read PNGs, optional resize
117
+ const bufs = await Promise.all(
118
+ inFiles.map(async (p) => {
119
+ const b = await readFile(p);
120
+ return maybeResize(b, size);
121
+ })
122
+ );
123
+
124
+ try {
125
+ const icoBuf = await pngToIco(bufs);
126
+ const outPath = resolve(out);
127
+ await writeFile(outPath, icoBuf);
128
+ const sizeMsg = size ? ` (resized to ${size}x${size})` : '';
129
+ console.log(`✔ Wrote ICO: ${outPath} from ${inFiles.length} PNG file(s)${sizeMsg}.`);
130
+ } catch (e) {
131
+ console.error('✖ Failed to build ICO:', e?.message || e);
132
+ process.exit(1);
133
+ }
134
+ }
135
+
136
+ main().catch((err) => {
137
+ console.error('✖ Error:', err?.message || err);
138
+ process.exit(1);
139
+ });
@@ -0,0 +1,42 @@
1
+ // Small helpers to parse and normalize build/run modes across scripts.
2
+
3
+ // Extracts mode from argv supporting "--mode <val>" and "--mode=<val>"
4
+ export function parseModeArg(argv = []) {
5
+ const idx = argv.findIndex((a) => a === '--mode');
6
+ if (idx !== -1 && argv[idx + 1]) return argv[idx + 1];
7
+ const eq = argv.find((a) => a.startsWith('--mode='));
8
+ if (eq) return eq.slice('--mode='.length);
9
+ return undefined;
10
+ }
11
+
12
+ // Normalizes common synonyms to canonical values and classifies composite modes.
13
+ // dev -> development, prod -> production
14
+ // development.* -> development, production.* -> production
15
+ export function normalizeMode(input) {
16
+ if (!input) return 'production';
17
+ const s = String(input).trim().toLowerCase();
18
+ if (s === 'prod' || s === 'production' || s.startsWith('production.')) return 'production';
19
+ if (s === 'dev' || s === 'development' || s.startsWith('development.') || s.startsWith('dev.')) return 'development';
20
+ return s;
21
+ }
22
+
23
+ // Resolves the effective mode from argv and environment WITHOUT normalizing,
24
+ // so Vite receives the full composite value (e.g., production.mac.arm64).
25
+ // Priority: argv -> MODE -> npm_config_mode -> NODE_ENV -> default
26
+ export function resolveMode(argv = [], env = process.env) {
27
+ const argMode = parseModeArg(argv);
28
+ const envMode = env.MODE;
29
+ const npmMode = env.npm_config_mode;
30
+ const nodeEnv = env.NODE_ENV;
31
+ return argMode || envMode || npmMode || nodeEnv || 'production';
32
+ }
33
+
34
+ // Detects a dry-run flag in argv.
35
+ export function parseDryRunArg(argv = []) {
36
+ return argv.some((a) => a === '--dry-run' || a === '--dryrun' || a === '--dry');
37
+ }
38
+
39
+ // Resolves whether to run in dry-run mode from argv and environment.
40
+ export function resolveDryRun(argv = [], env = process.env) {
41
+ return env.DRY_RUN === '1' || parseDryRunArg(argv);
42
+ }
@@ -0,0 +1,37 @@
1
+ import fs from 'fs/promises';
2
+ import path from 'path';
3
+ import sharp from 'sharp';
4
+
5
+ const SCREENSHOT_DIR = path.resolve(process.cwd(), './src');
6
+
7
+ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
8
+ async function processPng(filePath) {
9
+ const outputPath = filePath;
10
+ const buffer = await sharp(filePath)
11
+ .png({ force: true }) // 👈 без .withMetadata()
12
+ .toBuffer();
13
+ await fs.writeFile(outputPath, buffer);
14
+ }
15
+
16
+ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
17
+ async function processAllPngs(dir) {
18
+ const entries = await fs.readdir(dir, { withFileTypes: true });
19
+
20
+ // eslint-disable-next-line functional/no-loop-statements
21
+ for (const entry of entries) {
22
+ const fullPath = path.join(dir, entry.name);
23
+ if (entry.isDirectory()) {
24
+ await processAllPngs(fullPath);
25
+ } else if (entry.name.toLowerCase().endsWith('.png')) {
26
+ await processPng(fullPath);
27
+ console.log(`🧼 Cleaned metadata: ${fullPath}`);
28
+ }
29
+ }
30
+ }
31
+
32
+ processAllPngs(SCREENSHOT_DIR)
33
+ .then(() => console.log('✅ Screenshot metadata cleanup complete'))
34
+ .catch((err) => {
35
+ console.error('❌ Failed to clean screenshots:', err);
36
+ process.exit(1);
37
+ });
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from 'fs/promises';
4
+ import path from 'path';
5
+
6
+ const [, , inputPath, outputPath] = process.argv;
7
+
8
+ if (!inputPath || !outputPath) {
9
+ console.error('❌ Usage: node rename-preload.js <input.js> <output.mjs>');
10
+ process.exit(1);
11
+ }
12
+
13
+ try {
14
+ const resolvedInput = path.resolve(inputPath);
15
+ const resolvedOutput = path.resolve(outputPath);
16
+
17
+ await fs.rename(resolvedInput, resolvedOutput);
18
+
19
+ console.log(`✅ Renamed:\n ${resolvedInput} →\n ${resolvedOutput}`);
20
+ } catch (err) {
21
+ console.error('❌ Rename failed:', err);
22
+ process.exit(1);
23
+ }
@@ -0,0 +1,120 @@
1
+ import { NodeIO } from '@gltf-transform/core';
2
+ import { exec } from 'child_process';
3
+ import fg from 'fast-glob';
4
+ import { promises as fs } from 'fs';
5
+ import path from 'path';
6
+ import sharp from 'sharp';
7
+ import { optimize as optimizeSvg } from 'svgo';
8
+ // eslint-disable-next-line spellcheck/spell-checker
9
+ import { promisify } from 'util';
10
+
11
+ // eslint-disable-next-line spellcheck/spell-checker
12
+ const execAsync = promisify(exec);
13
+
14
+ // Allow passing target directory as CLI arg; default to ./public
15
+ const cliArg = process.argv[2];
16
+ if (cliArg === '--help' || cliArg === '-h') {
17
+ console.log('Usage: node SanitizeAssets.js [targetDir]\nDefault targetDir is ./public');
18
+ process.exit(0);
19
+ }
20
+ const TARGET_DIR = path.resolve(process.cwd(), cliArg || './public');
21
+
22
+ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
23
+ async function cleanImage(filePath) {
24
+ const ext = path.extname(filePath).toLowerCase();
25
+ const image = sharp(filePath);
26
+ const buffer = ext === '.png' ? await image.png({ force: true }).toBuffer() : await image.jpeg({ force: true }).toBuffer();
27
+ await fs.writeFile(filePath, buffer);
28
+ console.log(`🧼 Cleaned ${ext.toUpperCase()}: ${filePath}`);
29
+ }
30
+
31
+ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
32
+ async function cleanSvg(filePath) {
33
+ const original = await fs.readFile(filePath, 'utf-8');
34
+ const result = optimizeSvg(original, {
35
+ multipass: true,
36
+ plugins: ['removeMetadata', 'removeTitle', 'removeDesc']
37
+ });
38
+ await fs.writeFile(filePath, result.data);
39
+ console.log(`🧼 Optimized SVG: ${filePath}`);
40
+ }
41
+
42
+ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
43
+ async function cleanMp3(filePath) {
44
+ const tmp = filePath + '.cleaned.mp3';
45
+ await execAsync(`ffmpeg -i "${filePath}" -map_metadata -1 -y "${tmp}"`);
46
+ await fs.rename(tmp, filePath);
47
+ console.log(`🧼 Stripped metadata from MP3: ${filePath}`);
48
+ }
49
+
50
+ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
51
+ function sanitizeDocument(doc) {
52
+ const root = doc.getRoot();
53
+
54
+ // eslint-disable-next-line functional/immutable-data
55
+ root.getAsset().generator = undefined;
56
+
57
+ [
58
+ root.listAccessors(),
59
+ root.listAnimations(),
60
+ root.listBuffers(),
61
+ // root.listBufferViews(),
62
+ root.listCameras(),
63
+ // root.listImages(),
64
+ root.listMaterials(),
65
+ root.listMeshes(),
66
+ root.listNodes(),
67
+ // root.listSamplers(),
68
+ root.listScenes(),
69
+ root.listSkins(),
70
+ root.listTextures()
71
+ ]
72
+ .flat()
73
+ .forEach((item) => {
74
+ item.setExtras(undefined);
75
+ });
76
+
77
+ // eslint-disable-next-line functional/no-loop-statements
78
+ for (const extension of root.listExtensions()) {
79
+ // eslint-disable-next-line spellcheck/spell-checker
80
+ root.unregisterExtension(extension);
81
+ }
82
+ }
83
+
84
+ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
85
+ async function cleanGlb(filePath) {
86
+ try {
87
+ const io = new NodeIO();
88
+ const doc = await io.read(filePath);
89
+ sanitizeDocument(doc);
90
+ await io.write(filePath, doc);
91
+ console.log(`🧼 Cleaned GLB metadata: ${filePath}`);
92
+ } catch (err) {
93
+ console.warn(`⚠️ Failed to clean GLB: ${filePath}`, err.message);
94
+ }
95
+ }
96
+
97
+ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
98
+ async function sanitizeAssets() {
99
+ const files = await fg(['**/*.{png,jpg,jpeg,svg,mp3,ogg,glb,gltf}'], {
100
+ cwd: TARGET_DIR,
101
+ absolute: true
102
+ });
103
+
104
+ // eslint-disable-next-line functional/no-loop-statements
105
+ for (const file of files) {
106
+ const ext = path.extname(file).toLowerCase();
107
+ try {
108
+ if (['.png', '.jpg', '.jpeg'].includes(ext)) await cleanImage(file);
109
+ else if (ext === '.svg') await cleanSvg(file);
110
+ else if (['.mp3', '.ogg'].includes(ext)) await cleanMp3(file);
111
+ else if (['.glb'].includes(ext)) await cleanGlb(file);
112
+ } catch (err) {
113
+ console.warn(`⚠️ Failed to clean ${file}:`, err.message);
114
+ }
115
+ }
116
+
117
+ console.log('✅ Done: all assets sanitized');
118
+ }
119
+
120
+ sanitizeAssets();
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@hellpig/anarchy-shared",
3
3
  "author": "S. Panfilov",
4
4
  "private": false,
5
- "version": "1.5.2",
5
+ "version": "1.6.2",
6
6
  "type": "module",
7
7
  "repository": {
8
8
  "type": "git",
@@ -16,7 +16,9 @@
16
16
  "publishConfig": {
17
17
  "access": "public"
18
18
  },
19
- "sideEffects": false,
19
+ "sideEffects": [
20
+ "**/*.scss"
21
+ ],
20
22
  "license": "MIT",
21
23
  "description": "Anarchy-shared – a library with shared utilities and scripts for Anarchy Engine.",
22
24
  "keywords": [
@@ -31,7 +33,9 @@
31
33
  "NOTICE.md",
32
34
  "LICENSE",
33
35
  "README.md",
34
- "dist/"
36
+ "dist/",
37
+ "ScriptUtils/",
38
+ "src/assets/"
35
39
  ],
36
40
  "exports": {
37
41
  "./Utils": {
@@ -50,10 +54,6 @@
50
54
  "types": "./dist/Plugins/index.d.ts",
51
55
  "import": "./dist/Plugins/index.es.js"
52
56
  },
53
- "./assets": {
54
- "types": "./dist/assets/index.d.ts",
55
- "import": "./dist/assets/index.es.js"
56
- },
57
57
  "./assets/*": "./src/assets/*",
58
58
  "./ScriptUtils/*": "./ScriptUtils/*"
59
59
  },
File without changes
File without changes