diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c2658d7 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules/ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..863ef40 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,36 @@ +image: node:16-alpine3.14 + +cache: + key: "${CI_COMMIT_BRANCH}_${CI_JOB_NAME}_new" + paths: + - bin/node_modules/ + +splitter: + stage: deploy + before_script: + - npm i -g pnpm + - pnpm i -r + - git config --global user.name "CI" + - git config --global user.email "ci@c7.pm" + script: + - node bin/splitter.js xmc.user.css modular + - git add modular + - git commit -m "Make Modular" + - git push "https://${GITLAB_USER_LOGIN}:${CI_GIT_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git" "HEAD:${CI_DEFAULT_BRANCH}" + only: + changes: + - xmc.user.css + +pages: + stage: deploy + script: + - mkdir public + - cp -r modular public + - cp xmc.user.css public + - sed -i "s/@version 0.0.0/@version 0+${CI_COMMIT_HASH:0:8}/" public/xmc.user.css + artifactss: + paths: + - public + except: + changes: + - xmc.user.css diff --git a/bin/package.json b/bin/package.json new file mode 100644 index 0000000..640521c --- /dev/null +++ b/bin/package.json @@ -0,0 +1,13 @@ +{ + "name": "usercss-splitter", + "version": "0.0.1", + "description": "", + "main": "splitter.js", + "keywords": [], + "author": "Cynosphere", + "license": "MIT", + "dependencies": { + "mkdirp": "^1.0.4", + "usercss-meta": "^0.12.0" + } +} diff --git a/bin/pnpm-lock.yaml b/bin/pnpm-lock.yaml new file mode 100644 index 0000000..698293f --- /dev/null +++ b/bin/pnpm-lock.yaml @@ -0,0 +1,22 @@ +lockfileVersion: 5.4 + +specifiers: + mkdirp: ^1.0.4 + usercss-meta: ^0.12.0 + +dependencies: + mkdirp: 1.0.4 + usercss-meta: 0.12.0 + +packages: + + /mkdirp/1.0.4: + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} + engines: {node: '>=10'} + hasBin: true + dev: false + + /usercss-meta/0.12.0: + resolution: {integrity: sha512-zKrXCKdpeIwtVe87omxGo9URf+7mbozduMZEg79dmT4KB3XJwfIkEi/Uk0PcTwR/nZLtAK1+k7isgbGB/g6E7Q==} + engines: {node: '>=8.3'} + dev: false diff --git a/bin/splitter.js b/bin/splitter.js new file mode 100644 index 0000000..16650b8 --- /dev/null +++ b/bin/splitter.js @@ -0,0 +1,127 @@ +const mkdirp = require("mkdirp"); +const usercssMeta = require("usercss-meta"); + +const fs = require("fs"); +const path = require("path"); + +/****/ + +const inputFile = process.argv[1]; +const outputDir = process.argv[2]; + +if (!fs.existsSync(inputFile)) { + console.log("input does not exist"); + process.exit(1); +} +if (!fs.existsSync(outputDir)) mkdirp.sync(outputDir); + +const css = fs.readFileSync(inputFile, "utf8"); + +const files = {}; + +const preprocessors = { + default: true, + uso: true, +}; + +const usercssParser = usercssMeta.createParser({ + mandatoryKeys: [], + validateKey: { + preprocessor: (state) => { + const preprocessor = state.value ?? "default"; + if (!preprocessors[preprocessor]) + throw new usercssMeta.ParseError({ + message: `Unsupported preprocessor: ${preprocessor}`, + code: "unknownPreprocessor", + args: [state.value], + index: state.valueIndex, + }); + }, + }, + validateVar: { + select: (state) => { + if (state.varResult.options.every((o) => o.name !== state.value)) { + throw new usercssMeta.ParseError({ + code: "invalidSelectValueMismatch", + index: state.valueIndex, + }); + } + }, + }, +}); + +/****/ + +try { + //parser won't parse \r + const cssUnixEndings = css.replace(/\r/g, ""); + + const parsed = usercssParser.parse(cssUnixEndings); + if (!parsed?.metadata || Object.keys(parsed.metadata).length == 0) { + console.log("UserCSS file has no valid metadata."); + process.exit(1); + } + + if (parsed.errors.length > 0) { + console.log("UserCSS parsed with errors:"); + for (const error of parsed.errors) { + console.log(error); + } + } + + const sections = cssUnixEndings.match(/@-moz-document(.*?){\n(.*?\n)}/gs); + if (sections) { + for (const index in sections) { + const section = sections[index]; + const content = section.match(/@-moz-document(.*?){\n(.*?\n)}/s)[2]; + if (sections.length > 1) { + files["section-" + index] = content; + } else { + files["base"] = content; + } + } + } + + for (const key of Object.keys(parsed.metadata.vars)) { + const setting = parsed.metadata.vars[key]; + if (setting.type != "select") continue; + const options = setting.options.filter((x) => x.value != ""); + if (options.length > 1) { + for (const option of options) { + files[ + key.replace("xmc_", "") + + "/" + + option.name.toLowerCase().replace(/ /g, "_") + ] = option.value; + } + } else { + files[key.replace("xmc_", "")] = options[0].value; + } + } +} catch (err) { + console.log("UserCSS parsing failed: " + err); + process.exit(1); +} + +/****/ + +try { + for (const filePath of Object.keys(files)) { + const contents = files[filePath]; + if (filePath.indexOf("/") > -1) { + const [subfolder, name] = filePath.split("/"); + if (!fs.existsSync(path.join(outputDir, subfolder))) + mkdirp.sync(path.join(outputDir, subfolder)); + + fs.writeFileSync( + path.join(outputDir, subfolder, name + ".css"), + contents + ); + } else { + fs.writeFileSync(path.join(outputDir, name + ".css"), contents); + } + } +} catch (err) { + console.log("Failed to write files: " + err); + process.exit(1); +}