synthesisui 0.16.479 → 0.16.480
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/dist/colour-names.js +104 -0
- package/dist/commands/colours.js +101 -0
- package/dist/commands/init.js +17 -1
- package/dist/index.js +13 -0
- package/dist/new-project-theme.js +64 -0
- package/package.json +1 -1
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { blockMarks, readVocabulary } from "./new-project-theme.js";
|
|
4
|
+
/**
|
|
5
|
+
* A VOLTA PARA O CÓDIGO - etapa 05 da jornada "o projeto sem cor escolhe as suas", 28/09.
|
|
6
|
+
*
|
|
7
|
+
* Depois do "Use these colours", ele sai com UM comando e UM prompt. O comando escreve os seis nomes no CSS global
|
|
8
|
+
* dele, NA GRAFIA DELE - o prefixo que o projeto já usa, ou uma neutra quando não usa nenhum -, e pergunta uma vez
|
|
9
|
+
* antes. O prompt manda o agente trocar os valores escritos à mão pelos nomes. As cores não são do agente: foram
|
|
10
|
+
* decididas na tela.
|
|
11
|
+
*/
|
|
12
|
+
export const SIX_ROLES = [
|
|
13
|
+
"primary",
|
|
14
|
+
"canvas",
|
|
15
|
+
"surface",
|
|
16
|
+
"foreground",
|
|
17
|
+
"muted",
|
|
18
|
+
"border",
|
|
19
|
+
];
|
|
20
|
+
/** A palavra de cada papel no código dele - a que ele escreveria, e não o nosso identificador. */
|
|
21
|
+
const WORD = {
|
|
22
|
+
primary: { word: "primary", job: "buttons, links, what you click" },
|
|
23
|
+
canvas: { word: "background", job: "the page itself" },
|
|
24
|
+
surface: { word: "surface", job: "cards and panels" },
|
|
25
|
+
foreground: { word: "text", job: "what people read" },
|
|
26
|
+
muted: { word: "text-muted", job: "hints, captions, labels" },
|
|
27
|
+
border: { word: "border", job: "lines and edges" },
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* O PREFIXO DELE: quando a maioria dos nomes que ele declara começa pelo mesmo segmento (`--fn-space-1`,
|
|
31
|
+
* `--fn-radius-s`), é ali que os nomes novos entram (`--fn-primary`). Sem nome nenhum, ou sem um segmento que a
|
|
32
|
+
* maioria divida, a grafia é a neutra `--color-*` - a mesma que o `init` escreve num projeto novo.
|
|
33
|
+
*/
|
|
34
|
+
function prefixOf(own) {
|
|
35
|
+
if (own.length < 2)
|
|
36
|
+
return "--color-";
|
|
37
|
+
const counts = new Map();
|
|
38
|
+
for (const n of own) {
|
|
39
|
+
const m = /^--([a-z0-9]+)-/i.exec(n);
|
|
40
|
+
if (m)
|
|
41
|
+
counts.set(m[1], (counts.get(m[1]) ?? 0) + 1);
|
|
42
|
+
}
|
|
43
|
+
const [top, n] = [...counts].sort((a, b) => b[1] - a[1])[0] ?? ["", 0];
|
|
44
|
+
const generic = new Set([
|
|
45
|
+
"color",
|
|
46
|
+
"colors",
|
|
47
|
+
"space",
|
|
48
|
+
"spacing",
|
|
49
|
+
"radius",
|
|
50
|
+
"font",
|
|
51
|
+
"text",
|
|
52
|
+
"shadow",
|
|
53
|
+
]);
|
|
54
|
+
return n > own.length / 2 && !generic.has(top) ? `--${top}-` : "--color-";
|
|
55
|
+
}
|
|
56
|
+
export async function planColourNames(root, slug, roleHex) {
|
|
57
|
+
const v = await readVocabulary(root, slug);
|
|
58
|
+
const prefix = prefixOf([...v.own.keys()]);
|
|
59
|
+
const names = SIX_ROLES.filter((r) => roleHex[r]).map((role) => {
|
|
60
|
+
const name = `${prefix}${WORD[role].word}`;
|
|
61
|
+
const value = roleHex[role];
|
|
62
|
+
const have = v.own.get(name);
|
|
63
|
+
return {
|
|
64
|
+
role,
|
|
65
|
+
name,
|
|
66
|
+
value,
|
|
67
|
+
job: WORD[role].job,
|
|
68
|
+
exists: have != null && have.toLowerCase() === value.toLowerCase(),
|
|
69
|
+
};
|
|
70
|
+
});
|
|
71
|
+
return { file: v.global, prefix, names, css: v.css };
|
|
72
|
+
}
|
|
73
|
+
/** Escreve o bloco depois do sim dele - `ask` devolve a resposta; com o não, o arquivo fica como estava. */
|
|
74
|
+
export async function writeColourNames(root, slug, plan, ask) {
|
|
75
|
+
if (!plan.file)
|
|
76
|
+
return { written: false, reason: "no-file" };
|
|
77
|
+
const fresh = plan.names.filter((n) => !n.exists);
|
|
78
|
+
if (fresh.length === 0)
|
|
79
|
+
return { written: false, reason: "nothing-new" };
|
|
80
|
+
if (!(await ask()))
|
|
81
|
+
return { written: false, reason: "declined" };
|
|
82
|
+
const { begin, end, open, END_RE } = blockMarks;
|
|
83
|
+
const path = join(root, plan.file);
|
|
84
|
+
const css = await readFile(path, "utf8");
|
|
85
|
+
const block = [
|
|
86
|
+
begin(slug),
|
|
87
|
+
open(`${slug}: the six colours you chose, under names your code can use - var(${fresh[0]?.name}). They are yours.`),
|
|
88
|
+
":root {",
|
|
89
|
+
...fresh.map((n) => ` ${n.name}: ${n.value};`),
|
|
90
|
+
"}",
|
|
91
|
+
end(slug),
|
|
92
|
+
].join("\n");
|
|
93
|
+
const base = css.replace(END_RE(slug), "\n").replace(/\n*$/, "\n");
|
|
94
|
+
await writeFile(path, `${base}\n${block}\n`, "utf8");
|
|
95
|
+
return { written: true };
|
|
96
|
+
}
|
|
97
|
+
/** O PROMPT PRONTO - o que ele cola no agente. Cada valor à mão vira o nome; nenhuma cor nova, nenhum nome novo. */
|
|
98
|
+
export function agentPrompt(plan) {
|
|
99
|
+
return [
|
|
100
|
+
`Replace the colours written by hand in this project with the names declared in ${plan.file ?? "the global stylesheet"}:`,
|
|
101
|
+
...plan.names.map((n) => ` ${n.value} -> var(${n.name}) (${n.job})`),
|
|
102
|
+
"Change only a value that is exactly one of these. Leave every other colour as it is, and do not add or invent a name or a colour - these six were chosen by the person.",
|
|
103
|
+
].join("\n");
|
|
104
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { readdir, readFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { agentPrompt, planColourNames, SIX_ROLES, writeColourNames, } from "../colour-names.js";
|
|
4
|
+
import { body, section } from "../output.js";
|
|
5
|
+
import { RegistryError } from "../registry.js";
|
|
6
|
+
import { add } from "./add.js";
|
|
7
|
+
/**
|
|
8
|
+
* `synthesisui colours` - A VOLTA PARA O CÓDIGO, etapa 05 da jornada "o projeto sem cor escolhe as suas", 28/09.
|
|
9
|
+
*
|
|
10
|
+
* O comando que a tela de escolher cores entrega depois do "Use these colours". Ele traz a versão nova do sistema,
|
|
11
|
+
* mostra as seis cores com os nomes que vão entrar no CSS global dele, PERGUNTA UMA VEZ, escreve - e imprime o prompt
|
|
12
|
+
* que manda o agente trocar os valores escritos à mão pelos nomes. Com o não, nada muda no arquivo.
|
|
13
|
+
*/
|
|
14
|
+
export async function colours(asked, opts) {
|
|
15
|
+
const root = opts.dir ?? process.cwd();
|
|
16
|
+
const slug = asked ?? (await onlyInstalled(root));
|
|
17
|
+
/** A versão publicada pelo "Use these colours" - a instalada aqui ainda é a de antes. */
|
|
18
|
+
await add(slug, { registry: opts.registry, dir: root, setupHints: false });
|
|
19
|
+
const slugDir = join(root, "_synthesisui", "ds", slug);
|
|
20
|
+
const lock = JSON.parse(await readFile(join(slugDir, ".lock"), "utf8").catch(() => "{}"));
|
|
21
|
+
const tokens = typeof lock.version === "number"
|
|
22
|
+
? await readFile(join(slugDir, `v${lock.version}`, "tokens.css"), "utf8").catch(() => "")
|
|
23
|
+
: "";
|
|
24
|
+
const roleHex = roleColours(tokens);
|
|
25
|
+
const plan = await planColourNames(root, slug, roleHex);
|
|
26
|
+
console.log("");
|
|
27
|
+
console.log(section("Your six colours, back in your code"));
|
|
28
|
+
if (!plan.file) {
|
|
29
|
+
console.log(body("No global stylesheet was found here (a globals.css, or a single stylesheet that is not a module) - nothing was written. The prompt below still works: point your agent at the file where your colours should live."));
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
console.log(body(`These go into ${plan.file}, under names your code can use:`));
|
|
33
|
+
for (const n of plan.names)
|
|
34
|
+
console.log(body(` ${n.name}: ${n.value} ${n.exists ? "(already there)" : `- ${n.job}`}`));
|
|
35
|
+
}
|
|
36
|
+
const result = await writeColourNames(root, slug, plan, () => opts.yes
|
|
37
|
+
? Promise.resolve(true)
|
|
38
|
+
: askOnce(` Write them into ${plan.file}? [y/N] `));
|
|
39
|
+
console.log("");
|
|
40
|
+
if (result.written)
|
|
41
|
+
console.log(body(`✓ ${plan.file}: the six colours are declared, in your names.`));
|
|
42
|
+
else if (result.reason === "declined")
|
|
43
|
+
console.log(body(`Nothing was written - ${plan.file} is as it was. Run this again with --yes when you want them there.`));
|
|
44
|
+
else if (result.reason === "nothing-new")
|
|
45
|
+
console.log(body(`${plan.file} already declares all six with these values - nothing to write.`));
|
|
46
|
+
console.log("");
|
|
47
|
+
console.log(section("Paste this to your agent"));
|
|
48
|
+
for (const line of agentPrompt(plan).split("\n"))
|
|
49
|
+
console.log(body(line));
|
|
50
|
+
}
|
|
51
|
+
/** As seis cores do sistema, pelos papéis - seguindo as referências da folha até o valor. */
|
|
52
|
+
export function roleColours(tokensCss) {
|
|
53
|
+
const base = /\[data-ds="[^"]+"\]\s*\{([^}]*)\}/.exec(tokensCss)?.[1] ?? tokensCss;
|
|
54
|
+
const decl = new Map();
|
|
55
|
+
for (const m of base.matchAll(/(--ds-[a-z0-9-]+)\s*:\s*([^;]+);/gi))
|
|
56
|
+
decl.set(m[1], m[2].trim());
|
|
57
|
+
const resolve = (value, seen = new Set()) => {
|
|
58
|
+
const ref = /^var\(\s*(--ds-[a-z0-9-]+)\s*\)$/i.exec(value);
|
|
59
|
+
if (!ref || seen.has(ref[1]))
|
|
60
|
+
return value;
|
|
61
|
+
seen.add(ref[1]);
|
|
62
|
+
const next = decl.get(ref[1]);
|
|
63
|
+
return next ? resolve(next, seen) : value;
|
|
64
|
+
};
|
|
65
|
+
const out = {};
|
|
66
|
+
for (const role of SIX_ROLES) {
|
|
67
|
+
const v = decl.get(`--ds-color-semantic-${role}`);
|
|
68
|
+
if (v) {
|
|
69
|
+
const hex = resolve(v);
|
|
70
|
+
if (!hex.includes("var("))
|
|
71
|
+
out[role] = hex;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return out;
|
|
75
|
+
}
|
|
76
|
+
async function onlyInstalled(root) {
|
|
77
|
+
const entries = await readdir(join(root, "_synthesisui", "ds"), {
|
|
78
|
+
withFileTypes: true,
|
|
79
|
+
}).catch(() => []);
|
|
80
|
+
const slugs = entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
81
|
+
if (slugs.length === 1)
|
|
82
|
+
return slugs[0];
|
|
83
|
+
if (slugs.length === 0)
|
|
84
|
+
throw new RegistryError("No design system is installed here - run `synthesisui add <slug>` first.");
|
|
85
|
+
throw new RegistryError(`More than one system is installed here (${slugs.join(", ")}) - name the one: \`synthesisui colours <slug>\`.`);
|
|
86
|
+
}
|
|
87
|
+
async function askOnce(question) {
|
|
88
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY)
|
|
89
|
+
return false;
|
|
90
|
+
const { createInterface } = await import("node:readline/promises");
|
|
91
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
92
|
+
try {
|
|
93
|
+
return /^y(es)?$/i.test((await rl.question(question)).trim());
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
finally {
|
|
99
|
+
rl.close();
|
|
100
|
+
}
|
|
101
|
+
}
|
package/dist/commands/init.js
CHANGED
|
@@ -4,7 +4,7 @@ import { resolveDeps, stylesFor, tailwindMajor } from "../stack.js";
|
|
|
4
4
|
import { formMix, measuredStyle } from "../styles-flavour.js";
|
|
5
5
|
import { readFile } from "node:fs/promises";
|
|
6
6
|
import { join } from "node:path";
|
|
7
|
-
import { readProjectTheme, writeBesideTheirs, writeNewProjectTheme } from "../new-project-theme.js";
|
|
7
|
+
import { readProjectTheme, writeBesideTheirs, writeNewProjectRoot, writeNewProjectTheme } from "../new-project-theme.js";
|
|
8
8
|
import { add } from "./add.js";
|
|
9
9
|
const whereOf = (answer) => answer === "file" || answer === "1"
|
|
10
10
|
? "file"
|
|
@@ -216,6 +216,22 @@ export async function init(opts) {
|
|
|
216
216
|
}
|
|
217
217
|
}
|
|
218
218
|
}
|
|
219
|
+
/**
|
|
220
|
+
* E O PROJETO SEM TAILWIND TAMBÉM - etapa 05 da jornada "o projeto sem cor escolhe as suas" (28/09). No b10 da
|
|
221
|
+
* bateria o `init --ds` com CSS puro não escrevia nome nenhum, e o agente escreveu 79 valores crus. Mesma trava do
|
|
222
|
+
* caminho acima: só no projeto que ainda não nomeia nada seu.
|
|
223
|
+
*/
|
|
224
|
+
if (project !== "tailwind") {
|
|
225
|
+
const slugDir = join(root, "_synthesisui", "ds", opts.ds);
|
|
226
|
+
const lock = JSON.parse(await readFile(join(slugDir, ".lock"), "utf8").catch(() => "{}"));
|
|
227
|
+
const version = typeof lock.version === "number" ? lock.version : null;
|
|
228
|
+
const tokens = version !== null ? await readFile(join(slugDir, `v${version}`, "tokens.css"), "utf8").catch(() => "") : "";
|
|
229
|
+
const theme = tokens && version !== null ? await writeNewProjectRoot(root, tokens, opts.ds, version) : null;
|
|
230
|
+
if (theme?.written) {
|
|
231
|
+
console.log("");
|
|
232
|
+
console.log(`✓ ${theme.written}: ${theme.count} names of ${opts.ds} declared in your own :root, with the values written - your project had none of its own. They are yours: var(--color-primary) and var(--spacing-md) now paint the system.`);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
219
235
|
/**
|
|
220
236
|
* ERAM DOIS PASSOS E É UM - `INV-GERAL-13`, 11/09.
|
|
221
237
|
*
|
package/dist/index.js
CHANGED
|
@@ -31,6 +31,7 @@ import { summary } from "./commands/summary.js";
|
|
|
31
31
|
import { sync } from "./commands/sync.js";
|
|
32
32
|
import { template } from "./commands/template.js";
|
|
33
33
|
import { upgrade } from "./commands/upgrade.js";
|
|
34
|
+
import { colours } from "./commands/colours.js";
|
|
34
35
|
import { use } from "./commands/use.js";
|
|
35
36
|
import { appendEvent } from "./doctor/ledger.js";
|
|
36
37
|
import { blueprintTarget, installedSlugs } from "./installed.js";
|
|
@@ -57,6 +58,7 @@ Usage - deterministic, FREE:
|
|
|
57
58
|
synthesisui template <slug> <name> materialize a whole page from a DS template
|
|
58
59
|
(--as landing-home names the output - multi-page safe)
|
|
59
60
|
synthesisui upgrade <slug> update an installed DS + regenerate your components + migration brief
|
|
61
|
+
synthesisui colours [slug] write the colours you chose on the site into your CSS, in your names (asks first)
|
|
60
62
|
synthesisui use <slug> "<intent>" print a ready-to-paste agent prompt to build/modify on-system
|
|
61
63
|
synthesisui clean [--force] strip create-next-app boilerplate (dry run without --force)
|
|
62
64
|
|
|
@@ -680,6 +682,17 @@ async function main() {
|
|
|
680
682
|
});
|
|
681
683
|
break;
|
|
682
684
|
}
|
|
685
|
+
case "colours":
|
|
686
|
+
case "colors": {
|
|
687
|
+
/** Etapa 05 da jornada "o projeto sem cor escolhe as suas" (28/09): o comando que a tela de cores entrega. */
|
|
688
|
+
/** `--yes teste-b2` chega com o nome como valor da flag - ver `boolFlag`. */
|
|
689
|
+
await colours(args[0] ?? (typeof flags.yes === "string" ? flags.yes : undefined), {
|
|
690
|
+
registry,
|
|
691
|
+
dir,
|
|
692
|
+
yes: boolFlag(flags, "yes"),
|
|
693
|
+
});
|
|
694
|
+
break;
|
|
695
|
+
}
|
|
683
696
|
case "upgrade": {
|
|
684
697
|
/**
|
|
685
698
|
* O SLUG É OPCIONAL desde 07/08: `upgrade` deixou de ser ocasional e virou O comando de
|
|
@@ -106,6 +106,70 @@ export async function writeNewProjectTheme(root, tokensCss, slug, version) {
|
|
|
106
106
|
await writeFile(path, `${base}\n${block}\n`, "utf8");
|
|
107
107
|
return { written: relative(root, path).split("\\").join("/"), count: lines.length };
|
|
108
108
|
}
|
|
109
|
+
/**
|
|
110
|
+
* O VOCABULÁRIO DELE E O CSS GLOBAL, para quem escreve nomes fora do `init` - o `colours` (etapa 05, 28/09). A mesma
|
|
111
|
+
* leitura de `readTheirs`, com a mesma regra de qual folha é a global.
|
|
112
|
+
*/
|
|
113
|
+
export async function readVocabulary(root, slug) {
|
|
114
|
+
const r = await readTheirs(root, slug);
|
|
115
|
+
const own = new Map([...r.own].map((n) => [n, r.values.get(n) ?? ""]));
|
|
116
|
+
const path = globalSheetOf(r.sheets);
|
|
117
|
+
return {
|
|
118
|
+
own,
|
|
119
|
+
global: path ? relative(root, path).split("\\").join("/") : null,
|
|
120
|
+
css: path ? (r.sheets.get(path) ?? "") : "",
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
function globalSheetOf(sheets) {
|
|
124
|
+
const globals = [...sheets.keys()].filter((p) => !/\.module\.css$/i.test(p));
|
|
125
|
+
const named = globals
|
|
126
|
+
.filter((p) => /(^|[\\/])globals\.css$/i.test(p))
|
|
127
|
+
.sort((a, b) => a.split(/[\\/]/).length - b.split(/[\\/]/).length);
|
|
128
|
+
return named[0] ?? (globals.length === 1 ? globals[0] : undefined);
|
|
129
|
+
}
|
|
130
|
+
/** Os marcadores do bloco, para o `colours` escrever e reescrever o seu no mesmo lugar. */
|
|
131
|
+
export const blockMarks = { begin, end, open, END_RE };
|
|
132
|
+
/**
|
|
133
|
+
* O MESMO TEMA NO PROJETO NOVO COM CSS PURO - etapa 05 da jornada "o projeto sem cor escolhe as suas", 28/09.
|
|
134
|
+
*
|
|
135
|
+
* O item 5 da triagem da bateria: no b10 (`init --ds` num Next sem Tailwind) nenhum nome foi escrito, e o agente
|
|
136
|
+
* escreveu 79 valores crus; com Tailwind (b11) o mesmo init escrevia 92 nomes no `@theme`. Aqui vai um `:root` no CSS
|
|
137
|
+
* global dele, com os MESMOS nomes (`--color-primary`, `--spacing-md`) e os valores escritos. Sem o `@theme` eles não
|
|
138
|
+
* viram utilitário - num projeto de CSS puro, o uso é `var(--color-primary)`, que é o que ele escreveria.
|
|
139
|
+
*
|
|
140
|
+
* A MESMA TRAVA: só no projeto que não nomeia nada próprio. O CSS global é o `globals.css` (o mais raso, se houver
|
|
141
|
+
* mais de um), ou a única folha que não é módulo.
|
|
142
|
+
*/
|
|
143
|
+
export async function writeNewProjectRoot(root, tokensCss, slug, version) {
|
|
144
|
+
const r = await readTheirs(root, slug);
|
|
145
|
+
if (r.own.size > 0)
|
|
146
|
+
return { written: null, count: 0, reason: "own-vocabulary" };
|
|
147
|
+
const path = globalSheetOf(r.sheets);
|
|
148
|
+
if (!path)
|
|
149
|
+
return { written: null, count: 0, reason: "no-tailwind-entry" };
|
|
150
|
+
const css = r.sheets.get(path) ?? "";
|
|
151
|
+
const system = new Map(systemNames(tokensCss, r.every));
|
|
152
|
+
const lines = [...system].map(([n, v]) => ` ${n}: ${v};`);
|
|
153
|
+
if (lines.length === 0)
|
|
154
|
+
return { written: null, count: 0, reason: "empty-sheet" };
|
|
155
|
+
const canvas = system.get("--color-canvas") ?? system.get("--color-surface");
|
|
156
|
+
const text = system.get("--color-foreground");
|
|
157
|
+
if (canvas && /--background\s*:/.test(css))
|
|
158
|
+
lines.push(` --background: ${canvas};`);
|
|
159
|
+
if (text && /--foreground\s*:/.test(css))
|
|
160
|
+
lines.push(` --foreground: ${text};`);
|
|
161
|
+
const block = [
|
|
162
|
+
begin(slug),
|
|
163
|
+
open(`${slug} v${version}: the system's values under plain names - use them as var(--color-primary). They are yours now: change a value here and every component follows. Written by \`init\`, because this project had no tokens of its own.`),
|
|
164
|
+
":root {",
|
|
165
|
+
...lines,
|
|
166
|
+
"}",
|
|
167
|
+
end(slug),
|
|
168
|
+
].join("\n");
|
|
169
|
+
const base = css.replace(END_RE(slug), "\n").replace(/\n*$/, "\n");
|
|
170
|
+
await writeFile(path, `${base}\n${block}\n`, "utf8");
|
|
171
|
+
return { written: relative(root, path).split("\\").join("/"), count: lines.length };
|
|
172
|
+
}
|
|
109
173
|
const importLine = (slug) => `@import "./${slug}.css";`;
|
|
110
174
|
/** O CSS dele sem nada nosso: sem o bloco e sem a linha que importa o arquivo à parte. */
|
|
111
175
|
const withoutOurs = (css, slug) => css.replace(END_RE(slug), "\n").replace(new RegExp(`^${escape(importLine(slug))}\\n`, "m"), "");
|
package/package.json
CHANGED