@terrazzo/plugin-tailwind 2.0.0-beta.5 → 2.0.0-beta.6
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/index.d.ts +66 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +93 -38
- package/dist/index.js.map +1 -1
- package/package.json +10 -9
package/dist/index.d.ts
CHANGED
|
@@ -5,16 +5,59 @@ declare const FORMAT_ID = "tailwind";
|
|
|
5
5
|
declare const PLUGIN_NAME = "@terrazzo/plugin-tailwind";
|
|
6
6
|
type ResolverInput = Record<string, string>;
|
|
7
7
|
interface TailwindPluginOptions {
|
|
8
|
+
/**
|
|
9
|
+
* Path to a template file.
|
|
10
|
+
*
|
|
11
|
+
* @example "tailwind.template.css";
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```css
|
|
15
|
+
* @import "tailwindcss";
|
|
16
|
+
* /* Default theme *\/
|
|
17
|
+
* @theme {
|
|
18
|
+
* @tz (theme: "light");
|
|
19
|
+
* }
|
|
20
|
+
* /* Uncomment to change conditions for dark mode *\/
|
|
21
|
+
* /* @custom-variant dark ([data-theme="dark"]); *\/
|
|
22
|
+
*
|
|
23
|
+
* /* Dark mode (@see https://tailwindcss.com/docs/dark-mode) *\/
|
|
24
|
+
* @variant dark {
|
|
25
|
+
* @tz (theme: "dark");
|
|
26
|
+
* }
|
|
27
|
+
*
|
|
28
|
+
* /* Custom variant: light-high-contrast (shortened to "light-hc" in Tailwind) *\/
|
|
29
|
+
* @custom-variant light-hc ([data-theme="light-hc"]);
|
|
30
|
+
*
|
|
31
|
+
* @variant light-hc {
|
|
32
|
+
* @tz (theme: "light-high-contrast");
|
|
33
|
+
* }
|
|
34
|
+
*
|
|
35
|
+
* /* Custom variant: dark-high-contrast (shortened to "dark-hc" in Tailwind) *\/
|
|
36
|
+
* @custom-variant dark-hc ([data-theme="dark-hc"]);
|
|
37
|
+
*
|
|
38
|
+
* @variant dark-hc {
|
|
39
|
+
* @tz (theme: "dark-high-contrast");
|
|
40
|
+
* }
|
|
41
|
+
*
|
|
42
|
+
* /* Custom variant for reduced motion *\/
|
|
43
|
+
* @custom-variant reduced-motion (@media (prefers-reduced-motion: reduce));
|
|
44
|
+
*
|
|
45
|
+
* @variant reduced-motion {
|
|
46
|
+
* @tz (motion: "reduced");
|
|
47
|
+
* }
|
|
48
|
+
*
|
|
49
|
+
* /* Custom CSS is allowed *\/
|
|
50
|
+
* .my-custom-util {
|
|
51
|
+
* color: red;
|
|
52
|
+
* }
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
template: string;
|
|
8
56
|
/**
|
|
9
57
|
* Filename to output.
|
|
10
58
|
* @default "tailwind-theme.css"
|
|
11
59
|
*/
|
|
12
60
|
filename?: string;
|
|
13
|
-
/**
|
|
14
|
-
* Path to a template file. The template should contain `@terrazzo-slot;`
|
|
15
|
-
* which will be replaced with the generated theme.
|
|
16
|
-
*/
|
|
17
|
-
template?: string;
|
|
18
61
|
/** @see https://tailwindcss.com/docs/theme */
|
|
19
62
|
theme: Record<string, unknown>;
|
|
20
63
|
/** Default permutation */
|
|
@@ -37,11 +80,26 @@ declare function flattenThemeObj(themeObj: Record<string, unknown>): {
|
|
|
37
80
|
}[];
|
|
38
81
|
declare const FILE_HEADER = "/* -------------------------------------------\n * Autogenerated by \u26CB Terrazzo. DO NOT EDIT!\n * ------------------------------------------- */";
|
|
39
82
|
declare function buildFileHeader(templatePath?: string): string;
|
|
40
|
-
|
|
41
|
-
|
|
83
|
+
interface TzAtRule {
|
|
84
|
+
start: number;
|
|
85
|
+
end: number;
|
|
86
|
+
input: Record<string, string>;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Parse @tz at-rules in CSS.
|
|
90
|
+
*/
|
|
91
|
+
declare function parseTzAtRules(css: string): TzAtRule[];
|
|
92
|
+
/**
|
|
93
|
+
* Parse an individual @tz at-rule in CSS.
|
|
94
|
+
*
|
|
95
|
+
* This algorithm requires 2 passes:
|
|
96
|
+
* 1. Determine the beginning and end of the expression, accounting for arbitrary whitespace, comments, and even CSS-omittable semicolons
|
|
97
|
+
* 2. Take the inner body of the at-rule and parse the parameters.
|
|
98
|
+
*/
|
|
99
|
+
declare function parseTzAtRule(css: string): TzAtRule | undefined;
|
|
42
100
|
//#endregion
|
|
43
101
|
//#region src/index.d.ts
|
|
44
102
|
declare function pluginTailwind(options: TailwindPluginOptions): Plugin;
|
|
45
103
|
//#endregion
|
|
46
|
-
export { FILE_HEADER, FORMAT_ID, PLUGIN_NAME, ResolverInput,
|
|
104
|
+
export { FILE_HEADER, FORMAT_ID, PLUGIN_NAME, ResolverInput, TailwindPluginOptions, TzAtRule, buildFileHeader, pluginTailwind as default, flattenThemeObj, parseTzAtRule, parseTzAtRules };
|
|
47
105
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/lib.ts","../src/index.ts"],"mappings":";;;cAAa,SAAA;AAAA,cACA,WAAA;AAAA,KAED,aAAA,GAAgB,MAAA;AAAA,UAEX,qBAAA;EALK;;;;
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/lib.ts","../src/index.ts"],"mappings":";;;cAAa,SAAA;AAAA,cACA,WAAA;AAAA,KAED,aAAA,GAAgB,MAAA;AAAA,UAEX,qBAAA;EALK;;;;AACtB;;;;;AAEA;;;;;AAEA;;;;;;;;;;;;;;;;;;;;;AAyEA;;;;;;;;;;AAuBA;;EAhDE,QAAA;EAgDsB;;AAIxB;;EA/CE,QAAA;EA+C8B;EA7C9B,KAAA,EAAO,MAAA;EAuDQ;EArDf,YAAA,GAAe,aAAA;;;;;EAKf,cAAA;IAAA,CACG,IAAA;MAkDU,gDAhDT,QAAA,UAsDU;MApDV,KAAA,EAAO,aAAA;IAAA;EAAA;AAAA;AA2Eb;AAAA,iBArEgB,eAAA,CAAgB,QAAA,EAAU,MAAA;EAA4B,IAAA;EAAgB,KAAA;AAAA;AAAA,cAuBzE,WAAA;AAAA,iBAIG,eAAA,CAAgB,YAAA;AAAA,UAUf,QAAA;EACf,KAAA;EACA,GAAA;EACA,KAAA,EAAO,MAAA;AAAA;;;;iBAMO,cAAA,CAAe,GAAA,WAAc,QAAA;;;;;;;;iBAuB7B,aAAA,CAAc,GAAA,WAAc,QAAA;;;iBClIpB,cAAA,CAAe,OAAA,EAAS,qBAAA,GAAwB,MAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import fsSync from "node:fs";
|
|
2
2
|
import fs from "node:fs/promises";
|
|
3
|
-
import { fileURLToPath } from "node:url";
|
|
4
3
|
import { FORMAT_ID as FORMAT_ID$1 } from "@terrazzo/plugin-css";
|
|
5
4
|
import { makeCSSVar } from "@terrazzo/token-tools/css";
|
|
6
5
|
|
|
@@ -35,25 +34,84 @@ function buildFileHeader(templatePath) {
|
|
|
35
34
|
* ------------------------------------------- */`;
|
|
36
35
|
return FILE_HEADER;
|
|
37
36
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
37
|
+
/**
|
|
38
|
+
* Parse @tz at-rules in CSS.
|
|
39
|
+
*/
|
|
40
|
+
function parseTzAtRules(css) {
|
|
41
|
+
let i = 0;
|
|
42
|
+
const atRules = [];
|
|
43
|
+
while (i < css.length) {
|
|
44
|
+
const next = parseTzAtRule(css.slice(i));
|
|
45
|
+
if (!next) break;
|
|
46
|
+
next.start += i;
|
|
47
|
+
next.end += i;
|
|
48
|
+
atRules.push(next);
|
|
49
|
+
i = next.end;
|
|
50
|
+
}
|
|
51
|
+
return atRules;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Parse an individual @tz at-rule in CSS.
|
|
55
|
+
*
|
|
56
|
+
* This algorithm requires 2 passes:
|
|
57
|
+
* 1. Determine the beginning and end of the expression, accounting for arbitrary whitespace, comments, and even CSS-omittable semicolons
|
|
58
|
+
* 2. Take the inner body of the at-rule and parse the parameters.
|
|
59
|
+
*/
|
|
60
|
+
function parseTzAtRule(css) {
|
|
61
|
+
if (!css.includes("@tz")) return;
|
|
62
|
+
let start = -1;
|
|
63
|
+
let end = -1;
|
|
64
|
+
const input = {};
|
|
65
|
+
for (let i = 0; i < css.length; i++) {
|
|
66
|
+
const char = css[i];
|
|
67
|
+
if (char === "/" && css[i + 1] === "*") {
|
|
68
|
+
const commentEnd = css.slice(i + 1).indexOf("*/");
|
|
69
|
+
i += commentEnd + 2;
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
if (char === "@" && css[i + 1] === "t" && css[i + 2] === "z" && !/[A-Za-z0-9]/.test(css[i + 3] || "")) start = i;
|
|
73
|
+
if (start !== -1) {
|
|
74
|
+
if (char === ";" || i === css.length - 1) {
|
|
75
|
+
end = i + 1;
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
if (char === "}") {
|
|
79
|
+
end = i;
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (start === -1) return;
|
|
85
|
+
const syntaxErr = /* @__PURE__ */ new Error(`Invalid syntax: ${css.slice(start, end)}. Expected @tz(modifier1: "value", modifier2: "value", …).`);
|
|
86
|
+
const bodyRaw = css.slice(start + 3, end).replace(/\/\*.*\*\//g, "").trim();
|
|
87
|
+
if (bodyRaw.includes("(") && !bodyRaw.includes(")") || !bodyRaw.includes("(") && bodyRaw.includes(")")) throw syntaxErr;
|
|
88
|
+
const body = bodyRaw.replace(/^\(\s*/, "").replace(/\s*[)}]?;?$/, "");
|
|
89
|
+
if (body) {
|
|
90
|
+
const params = body.split(",");
|
|
91
|
+
for (const param of params) {
|
|
92
|
+
const [name, value] = param.split(":");
|
|
93
|
+
if (!name || !value) throw syntaxErr;
|
|
94
|
+
try {
|
|
95
|
+
input[name.trim()] = JSON.parse(value.trim());
|
|
96
|
+
} catch {
|
|
97
|
+
throw syntaxErr;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
start,
|
|
103
|
+
end,
|
|
104
|
+
input
|
|
105
|
+
};
|
|
42
106
|
}
|
|
43
107
|
|
|
44
108
|
//#endregion
|
|
45
109
|
//#region src/index.ts
|
|
46
|
-
const DEFAULT_THEME = ".";
|
|
47
110
|
function pluginTailwind(options) {
|
|
48
111
|
const filename = options?.filename ?? "tailwind-theme.css";
|
|
49
|
-
const variations = {
|
|
50
|
-
[DEFAULT_THEME]: {
|
|
51
|
-
selector: "",
|
|
52
|
-
input: options.defaultTheme ?? { tzMode: "." }
|
|
53
|
-
},
|
|
54
|
-
...options?.customVariants
|
|
55
|
-
};
|
|
56
112
|
let cwd;
|
|
113
|
+
let template;
|
|
114
|
+
const tzAtRules = [];
|
|
57
115
|
const msg = {
|
|
58
116
|
group: "plugin",
|
|
59
117
|
label: PLUGIN_NAME
|
|
@@ -75,14 +133,20 @@ function pluginTailwind(options) {
|
|
|
75
133
|
message: "Migrate \"modeVariants\" to \"variants\" in config (see docs)"
|
|
76
134
|
});
|
|
77
135
|
cwd = new URL("./", config.outDir);
|
|
78
|
-
if (
|
|
136
|
+
if (!fsSync.existsSync(new URL(options.template, cwd))) logger.error({
|
|
79
137
|
...msg,
|
|
80
|
-
message: `Could not locate template ${
|
|
138
|
+
message: `Could not locate template "${options.template}". Does the file exist?`
|
|
81
139
|
});
|
|
82
140
|
},
|
|
83
141
|
async transform({ getTransforms, setTransform, context: { logger } }) {
|
|
142
|
+
template = await fs.readFile(options.template, "utf8");
|
|
143
|
+
if (!template.includes("@tz")) logger.error({
|
|
144
|
+
...msg,
|
|
145
|
+
message: `${options.template}: missing @tz helper! Terrazzo won’t generate any output for Tailwind. See https://terrazzo.app/docs/integrations/tailwind.`
|
|
146
|
+
});
|
|
147
|
+
tzAtRules.push(...parseTzAtRules(template));
|
|
84
148
|
const flatTheme = flattenThemeObj(options.theme);
|
|
85
|
-
for (const { input } of
|
|
149
|
+
for (const { input } of tzAtRules) {
|
|
86
150
|
const query = getTokenQuery(input);
|
|
87
151
|
for (const { path, value } of flatTheme) {
|
|
88
152
|
const variantTokens = getTransforms({
|
|
@@ -111,30 +175,17 @@ function pluginTailwind(options) {
|
|
|
111
175
|
}
|
|
112
176
|
},
|
|
113
177
|
async build({ getTransforms, outputFile }) {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
for (const token of getTransforms({
|
|
178
|
+
const reversedAtRules = [...tzAtRules].reverse();
|
|
179
|
+
let generatedTemplate = template;
|
|
180
|
+
for (const { start, end, input } of reversedAtRules) {
|
|
181
|
+
const tokens = getTransforms({
|
|
119
182
|
...getTokenQuery(input),
|
|
120
183
|
format: FORMAT_ID
|
|
121
|
-
})
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
let finalOutput = "";
|
|
125
|
-
if (options.template) {
|
|
126
|
-
const templateUrl = new URL(options.template, cwd);
|
|
127
|
-
const templateContent = await fs.readFile(fileURLToPath(templateUrl), "utf8");
|
|
128
|
-
finalOutput += applyTemplate(templateContent, generatedTheme);
|
|
129
|
-
} else {
|
|
130
|
-
finalOutput += "@import \"tailwindcss\";\n\n";
|
|
131
|
-
finalOutput += generatedTheme;
|
|
184
|
+
});
|
|
185
|
+
const indent = getIndentAtPos(template, start);
|
|
186
|
+
generatedTemplate = `${generatedTemplate.slice(0, start)}${tokens.map((t) => `${t.localID}: ${t.value};`).join(`\n${indent}`)}${generatedTemplate.slice(end)}`;
|
|
132
187
|
}
|
|
133
|
-
outputFile(filename,
|
|
134
|
-
buildFileHeader(options.template),
|
|
135
|
-
"",
|
|
136
|
-
finalOutput
|
|
137
|
-
].join("\n"));
|
|
188
|
+
outputFile(filename, `${buildFileHeader(options.template)}\n\n${generatedTemplate}`);
|
|
138
189
|
}
|
|
139
190
|
};
|
|
140
191
|
}
|
|
@@ -146,7 +197,11 @@ function isLegacyModes(input) {
|
|
|
146
197
|
function getTokenQuery(input) {
|
|
147
198
|
return isLegacyModes(input) ? { mode: input.tzMode ?? "." } : { input };
|
|
148
199
|
}
|
|
200
|
+
/** Get the current indent based on the previous line break from char */
|
|
201
|
+
function getIndentAtPos(text, pos) {
|
|
202
|
+
return text.slice(0, pos).match(/\n(( |\t)+)$/)?.[1] || "";
|
|
203
|
+
}
|
|
149
204
|
|
|
150
205
|
//#endregion
|
|
151
|
-
export { FILE_HEADER, FORMAT_ID, PLUGIN_NAME,
|
|
206
|
+
export { FILE_HEADER, FORMAT_ID, PLUGIN_NAME, buildFileHeader, pluginTailwind as default, flattenThemeObj, parseTzAtRule, parseTzAtRules };
|
|
152
207
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["FORMAT_CSS","FORMAT_TAILWIND"],"sources":["../src/lib.ts","../src/index.ts"],"sourcesContent":["export const FORMAT_ID = 'tailwind';\nexport const PLUGIN_NAME = '@terrazzo/plugin-tailwind';\n\nexport type ResolverInput = Record<string, string>;\n\nexport interface TailwindPluginOptions {\n /**\n * Filename to output.\n * @default \"tailwind-theme.css\"\n */\n filename?: string;\n /**\n * Path to a template file. The template should contain `@terrazzo-slot;`\n * which will be replaced with the generated theme.\n */\n template?: string;\n /** @see https://tailwindcss.com/docs/theme */\n theme: Record<string, unknown>;\n /** Default permutation */\n defaultTheme?: ResolverInput;\n /**\n * Associate Tailwind custom variants with Resolver permutations\n * @see https://tailwindcss.com/docs/dark-mode#toggling-dark-mode-manually\n */\n customVariants?: {\n [name: string]: {\n /** The CSS selector to apply to this variant */\n selector: string;\n /** The resolver input to load for this custom variant */\n input: ResolverInput;\n };\n };\n}\n\n/** Flatten an arbitrarily-nested object */\nexport function flattenThemeObj(themeObj: Record<string, unknown>): { path: string[]; value: string | string[] }[] {\n const result: { path: string[]; value: string | string[] }[] = [];\n\n function traverse(obj: Record<string, unknown>, path: string[]) {\n for (const [key, value] of Object.entries(obj)) {\n const newPath = [...path, key];\n if (typeof value === 'string' || Array.isArray(value)) {\n if (Array.isArray(value) && (value.length === 0 || value.some((v) => typeof v !== 'string'))) {\n throw new Error(\n `Invalid value at path \"${newPath.join('.')}\": expected a string or an array of strings, but got ${JSON.stringify(value)}`,\n );\n }\n result.push({ path: newPath, value });\n } else if (typeof value === 'object' && value !== null) {\n traverse(value as Record<string, unknown>, newPath);\n }\n }\n }\n\n traverse(themeObj, []);\n return result;\n}\n\nexport const FILE_HEADER = `/* -------------------------------------------\n * Autogenerated by ⛋ Terrazzo. DO NOT EDIT!\n * ------------------------------------------- */`;\n\nexport function buildFileHeader(templatePath?: string): string {\n if (templatePath) {\n return `/* -------------------------------------------\n * Autogenerated by ⛋ Terrazzo. DO NOT EDIT!\n * template: ${templatePath}\n * ------------------------------------------- */`;\n }\n return FILE_HEADER;\n}\n\nexport const TERRAZZO_SLOT = '@terrazzo-slot;';\n\nexport function applyTemplate(template: string, generatedTheme: string): string {\n if (!template.includes(TERRAZZO_SLOT)) {\n throw new Error(`Template must contain \"${TERRAZZO_SLOT}\" directive`);\n }\n // Replace slot and any following newlines (CRLF or LF) with theme + single newline\n return template.replace(/@terrazzo-slot;(\\r?\\n)+/, `${generatedTheme.trimEnd()}\\n\\n`);\n}\n","import fsSync from 'node:fs';\nimport fs from 'node:fs/promises';\nimport { fileURLToPath } from 'node:url';\nimport type { Plugin } from '@terrazzo/parser';\nimport { FORMAT_ID as FORMAT_CSS } from '@terrazzo/plugin-css';\nimport { makeCSSVar } from '@terrazzo/token-tools/css';\nimport {\n applyTemplate,\n buildFileHeader,\n FORMAT_ID as FORMAT_TAILWIND,\n flattenThemeObj,\n PLUGIN_NAME,\n type TailwindPluginOptions,\n} from './lib.js';\n\nexport * from './lib.js';\n\nconst DEFAULT_THEME = '.';\n\nexport default function pluginTailwind(options: TailwindPluginOptions): Plugin {\n const filename = options?.filename ?? 'tailwind-theme.css';\n const variations = {\n [DEFAULT_THEME]: { selector: '', input: options.defaultTheme ?? { tzMode: '.' } },\n ...options?.customVariants,\n };\n let cwd: URL;\n const msg = { group: 'plugin' as const, label: PLUGIN_NAME };\n\n return {\n name: PLUGIN_NAME,\n enforce: 'post', // ensure this comes after @terrazzo/plugin-css\n config(config, { logger }) {\n if (!config.plugins.some((p) => p.name === '@terrazzo/plugin-css')) {\n logger.error({\n ...msg,\n message:\n '@terrazzo/plugin-css missing! Please install and add to the plugins array to use the Tailwind plugin.',\n });\n }\n\n if (!options || !options.theme) {\n logger.error({ ...msg, message: 'Missing Tailwind `theme` option.' });\n }\n\n if (options && 'modeVariants' in options) {\n logger.error({ ...msg, message: 'Migrate \"modeVariants\" to \"variants\" in config (see docs)' });\n }\n\n // store cwd for template resolution (parent of outDir)\n cwd = new URL('./', config.outDir);\n if (options?.template && !fsSync.existsSync(new URL(options.template, cwd))) {\n logger.error({\n ...msg,\n message: `Could not locate template ${fileURLToPath(new URL(options.template, cwd))}. Does the file exist?`,\n });\n }\n },\n async transform({ getTransforms, setTransform, context: { logger } }) {\n const flatTheme = flattenThemeObj(options.theme);\n\n for (const { input } of Object.values(variations)) {\n const query = getTokenQuery(input);\n // Note: it’s important to remember that under-the-hood, getting/setting modes is NOT the same as having an { tzMode: value } input.\n // The former allows glob searching across all modes, the latter does not. Especially in the Tailwind plugin, confusing the two\n // will result in many dropped tokens.\n for (const { path, value } of flatTheme) {\n const variantTokens = getTransforms({ ...query, format: FORMAT_CSS, id: value });\n // Warn the user if they are trying to generate an empty Tailwind variant\n if (!variantTokens.length) {\n logger.warn({ ...msg, message: `${value} matched 0 tokens` });\n }\n\n for (const token of variantTokens) {\n let relName = token.id.split('.').at(-1)!;\n for (const subgroup of [...(Array.isArray(value) ? value : [value])]) {\n const match = subgroup.replace(/\\*.*/, '');\n relName = token.id.replace(match, '');\n }\n setTransform(token.id, {\n ...query,\n format: FORMAT_TAILWIND,\n localID: makeCSSVar(`${path.join('-')}-${relName.replace(/\\./g, '-')}`),\n value: typeof token.value === 'object' ? token.value['.']! : token.value,\n });\n }\n }\n }\n },\n async build({ getTransforms, outputFile }) {\n let generatedTheme = '';\n for (const [variant, { input, selector }] of Object.entries(variations)) {\n if (generatedTheme) {\n generatedTheme += '\\n'; // add extra line break if continuing\n }\n generatedTheme += `${variant === DEFAULT_THEME ? '@theme' : `@custom-variant ${variant} (${selector})`} {\\n`;\n for (const token of getTransforms({ ...getTokenQuery(input), format: FORMAT_TAILWIND })) {\n generatedTheme += ` ${token.localID}: ${token.value};\\n`;\n }\n generatedTheme += '}\\n';\n }\n\n // build the output combining theme and template\n let finalOutput = '';\n if (options.template) {\n const templateUrl = new URL(options.template, cwd);\n const templateContent = await fs.readFile(fileURLToPath(templateUrl), 'utf8');\n finalOutput += applyTemplate(templateContent, generatedTheme);\n } else {\n finalOutput += '@import \"tailwindcss\";\\n\\n';\n finalOutput += generatedTheme;\n }\n\n const header = buildFileHeader(options.template);\n outputFile(filename, [header, '', finalOutput].join('\\n'));\n },\n };\n}\n\n/** Convert modes to inputs */\nfunction isLegacyModes(input: Record<string, string>): boolean {\n return Object.keys(input).length === 0 && 'tzMode' in input;\n}\n\n/** Build query for both resolvers and legacy modes */\nfunction getTokenQuery(input: Record<string, string>) {\n return isLegacyModes(input) ? { mode: input.tzMode ?? '.' } : { input };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["FORMAT_CSS","FORMAT_TAILWIND"],"sources":["../src/lib.ts","../src/index.ts"],"sourcesContent":["export const FORMAT_ID = 'tailwind';\nexport const PLUGIN_NAME = '@terrazzo/plugin-tailwind';\n\nexport type ResolverInput = Record<string, string>;\n\nexport interface TailwindPluginOptions {\n /**\n * Path to a template file.\n *\n * @example \"tailwind.template.css\";\n *\n * @example\n * ```css\n * @import \"tailwindcss\";\n * /* Default theme *\\/\n * @theme {\n * @tz (theme: \"light\");\n * }\n * /* Uncomment to change conditions for dark mode *\\/\n * /* @custom-variant dark ([data-theme=\"dark\"]); *\\/\n *\n * /* Dark mode (@see https://tailwindcss.com/docs/dark-mode) *\\/\n * @variant dark {\n * @tz (theme: \"dark\");\n * }\n *\n * /* Custom variant: light-high-contrast (shortened to \"light-hc\" in Tailwind) *\\/\n * @custom-variant light-hc ([data-theme=\"light-hc\"]);\n *\n * @variant light-hc {\n * @tz (theme: \"light-high-contrast\");\n * }\n *\n * /* Custom variant: dark-high-contrast (shortened to \"dark-hc\" in Tailwind) *\\/\n * @custom-variant dark-hc ([data-theme=\"dark-hc\"]);\n *\n * @variant dark-hc {\n * @tz (theme: \"dark-high-contrast\");\n * }\n *\n * /* Custom variant for reduced motion *\\/\n * @custom-variant reduced-motion (@media (prefers-reduced-motion: reduce));\n *\n * @variant reduced-motion {\n * @tz (motion: \"reduced\");\n * }\n *\n * /* Custom CSS is allowed *\\/\n * .my-custom-util {\n * color: red;\n * }\n * ```\n */\n template: string;\n /**\n * Filename to output.\n * @default \"tailwind-theme.css\"\n */\n filename?: string;\n /** @see https://tailwindcss.com/docs/theme */\n theme: Record<string, unknown>;\n /** Default permutation */\n defaultTheme?: ResolverInput;\n /**\n * Associate Tailwind custom variants with Resolver permutations\n * @see https://tailwindcss.com/docs/dark-mode#toggling-dark-mode-manually\n */\n customVariants?: {\n [name: string]: {\n /** The CSS selector to apply to this variant */\n selector: string;\n /** The resolver input to load for this custom variant */\n input: ResolverInput;\n };\n };\n}\n\n/** Flatten an arbitrarily-nested object */\nexport function flattenThemeObj(themeObj: Record<string, unknown>): { path: string[]; value: string | string[] }[] {\n const result: { path: string[]; value: string | string[] }[] = [];\n\n function traverse(obj: Record<string, unknown>, path: string[]) {\n for (const [key, value] of Object.entries(obj)) {\n const newPath = [...path, key];\n if (typeof value === 'string' || Array.isArray(value)) {\n if (Array.isArray(value) && (value.length === 0 || value.some((v) => typeof v !== 'string'))) {\n throw new Error(\n `Invalid value at path \"${newPath.join('.')}\": expected a string or an array of strings, but got ${JSON.stringify(value)}`,\n );\n }\n result.push({ path: newPath, value });\n } else if (typeof value === 'object' && value !== null) {\n traverse(value as Record<string, unknown>, newPath);\n }\n }\n }\n\n traverse(themeObj, []);\n return result;\n}\n\nexport const FILE_HEADER = `/* -------------------------------------------\n * Autogenerated by ⛋ Terrazzo. DO NOT EDIT!\n * ------------------------------------------- */`;\n\nexport function buildFileHeader(templatePath?: string): string {\n if (templatePath) {\n return `/* -------------------------------------------\n * Autogenerated by ⛋ Terrazzo. DO NOT EDIT!\n * template: ${templatePath}\n * ------------------------------------------- */`;\n }\n return FILE_HEADER;\n}\n\nexport interface TzAtRule {\n start: number;\n end: number;\n input: Record<string, string>;\n}\n\n/**\n * Parse @tz at-rules in CSS.\n */\nexport function parseTzAtRules(css: string): TzAtRule[] {\n let i = 0;\n const atRules: TzAtRule[] = [];\n while (i < css.length) {\n const next = parseTzAtRule(css.slice(i));\n if (!next) {\n break;\n }\n next.start += i;\n next.end += i;\n atRules.push(next);\n i = next.end;\n }\n return atRules;\n}\n\n/**\n * Parse an individual @tz at-rule in CSS.\n *\n * This algorithm requires 2 passes:\n * 1. Determine the beginning and end of the expression, accounting for arbitrary whitespace, comments, and even CSS-omittable semicolons\n * 2. Take the inner body of the at-rule and parse the parameters.\n */\nexport function parseTzAtRule(css: string): TzAtRule | undefined {\n // Cheap optimization: don’t bother doing work if something _looks_ like it contains @tz.\n // But note that this will match comments and at-rules like \"@tzap\" so we have to parse\n // to verify it’s valid.\n if (!css.includes('@tz')) {\n return;\n }\n let start = -1;\n let end = -1;\n const input: Record<string, string> = {};\n\n // first pass: determine the end of the expression, taking comments into account as well as omitting semicolons\n for (let i = 0; i < css.length; i++) {\n const char = css[i];\n // skip over comments, while still keeping count\n if (char === '/' && css[i + 1] === '*') {\n const commentEnd = css.slice(i + 1).indexOf('*/');\n i += commentEnd + '*/'.length;\n continue;\n }\n\n // We’ve found a match (not in a comment!) begin the search\n if (char === '@' && css[i + 1] === 't' && css[i + 2] === 'z' && !/[A-Za-z0-9]/.test(css[i + 3] || '')) {\n start = i;\n }\n\n // Only calculate once we’ve found @tz outside a comment\n if (start !== -1) {\n // handle semi-colon or end-of-file\n if (char === ';' || i === css.length - 1) {\n end = i + 1;\n break;\n }\n // handle end of block\n if (char === '}') {\n end = i;\n break;\n }\n }\n }\n\n // We never found a valid @tz match; return\n if (start === -1) {\n return;\n }\n\n const syntaxErr = new Error(\n `Invalid syntax: ${css.slice(start, end)}. Expected @tz(modifier1: \"value\", modifier2: \"value\", …).`,\n );\n\n // second pass: now that we know where the expression ends, parse the inner body (if any), and extract the inputs\n const bodyRaw = css\n .slice(start + '@tz'.length, end)\n .replace(/\\/\\*.*\\*\\//g, '')\n .trim();\n // because we ignored parens in parsing, make sure we don’t have mismatched pairs\n if ((bodyRaw.includes('(') && !bodyRaw.includes(')')) || (!bodyRaw.includes('(') && bodyRaw.includes(')'))) {\n throw syntaxErr;\n }\n const body = bodyRaw\n .replace(/^\\(\\s*/, '') // discard opening paren, and any whitespace\n .replace(/\\s*[)}]?;?$/, ''); // discard closing paren, or terminating semicolon or bracket, along with whitespace\n if (body) {\n const params = body.split(',');\n for (const param of params) {\n const [name, value] = param.split(':');\n if (!name || !value) {\n throw syntaxErr;\n }\n try {\n input[name.trim()] = JSON.parse(value.trim());\n } catch {\n throw syntaxErr;\n }\n }\n }\n\n return {\n start,\n end,\n input,\n };\n}\n","import fsSync from 'node:fs';\nimport fs from 'node:fs/promises';\nimport type { Plugin } from '@terrazzo/parser';\nimport { FORMAT_ID as FORMAT_CSS } from '@terrazzo/plugin-css';\nimport { makeCSSVar } from '@terrazzo/token-tools/css';\nimport {\n buildFileHeader,\n FORMAT_ID as FORMAT_TAILWIND,\n flattenThemeObj,\n PLUGIN_NAME,\n parseTzAtRules,\n type TailwindPluginOptions,\n type TzAtRule,\n} from './lib.js';\n\nexport * from './lib.js';\n\nexport default function pluginTailwind(options: TailwindPluginOptions): Plugin {\n const filename = options?.filename ?? 'tailwind-theme.css';\n let cwd: URL;\n let template: string;\n const tzAtRules: TzAtRule[] = [];\n const msg = { group: 'plugin' as const, label: PLUGIN_NAME };\n\n return {\n name: PLUGIN_NAME,\n enforce: 'post', // ensure this comes after @terrazzo/plugin-css\n config(config, { logger }) {\n if (!config.plugins.some((p) => p.name === '@terrazzo/plugin-css')) {\n logger.error({\n ...msg,\n message:\n '@terrazzo/plugin-css missing! Please install and add to the plugins array to use the Tailwind plugin.',\n });\n }\n\n if (!options || !options.theme) {\n logger.error({ ...msg, message: 'Missing Tailwind `theme` option.' });\n }\n\n if (options && 'modeVariants' in options) {\n logger.error({ ...msg, message: 'Migrate \"modeVariants\" to \"variants\" in config (see docs)' });\n }\n\n // store cwd for template resolution (parent of outDir)\n cwd = new URL('./', config.outDir);\n if (!fsSync.existsSync(new URL(options.template, cwd))) {\n logger.error({ ...msg, message: `Could not locate template \"${options.template}\". Does the file exist?` });\n }\n },\n async transform({ getTransforms, setTransform, context: { logger } }) {\n // First, validate template, and parse the locations of all @tz at-rules.\n template = await fs.readFile(options.template, 'utf8');\n if (!template.includes('@tz')) {\n logger.error({\n ...msg,\n message: `${options.template}: missing @tz helper! Terrazzo won’t generate any output for Tailwind. See https://terrazzo.app/docs/integrations/tailwind.`,\n });\n }\n tzAtRules.push(...parseTzAtRules(template));\n\n // Next, iterate over the occurrences of @tz and generate the appropriate token values.\n const flatTheme = flattenThemeObj(options.theme);\n for (const { input } of tzAtRules) {\n const query = getTokenQuery(input);\n // Note: it’s important to remember that under-the-hood, getting/setting modes is NOT the same as having an { tzMode: value } input.\n // The former allows glob searching across all modes, the latter does not. Especially in the Tailwind plugin, confusing the two\n // will result in many dropped tokens.\n for (const { path, value } of flatTheme) {\n const variantTokens = getTransforms({ ...query, format: FORMAT_CSS, id: value });\n // Warn the user if they are trying to generate an empty Tailwind variant\n if (!variantTokens.length) {\n logger.warn({ ...msg, message: `${value} matched 0 tokens` });\n }\n\n for (const token of variantTokens) {\n let relName = token.id.split('.').at(-1)!;\n for (const subgroup of [...(Array.isArray(value) ? value : [value])]) {\n const match = subgroup.replace(/\\*.*/, '');\n relName = token.id.replace(match, '');\n }\n setTransform(token.id, {\n ...query,\n format: FORMAT_TAILWIND,\n localID: makeCSSVar(`${path.join('-')}-${relName.replace(/\\./g, '-')}`),\n value: typeof token.value === 'object' ? token.value['.']! : token.value,\n });\n }\n }\n }\n },\n async build({ getTransforms, outputFile }) {\n // Classic replacement hack: If we replace back-to-front, rather than\n // front-to-back, all our start/end locations will still be valid and we\n // won’t have to reparse the template every time.\n const reversedAtRules = [...tzAtRules].reverse();\n let generatedTemplate = template;\n for (const { start, end, input } of reversedAtRules) {\n const tokens = getTransforms({ ...getTokenQuery(input), format: FORMAT_TAILWIND });\n const indent = getIndentAtPos(template, start);\n generatedTemplate = `${generatedTemplate.slice(0, start)}${tokens.map((t) => `${t.localID}: ${t.value};`).join(`\\n${indent}`)}${generatedTemplate.slice(end)}`;\n }\n // Note: don’t append the header till the end, otherwise start/end will all be wrong\n outputFile(filename, `${buildFileHeader(options.template)}\\n\\n${generatedTemplate}`);\n },\n };\n}\n\n/** Convert modes to inputs */\nfunction isLegacyModes(input: Record<string, string>): boolean {\n return Object.keys(input).length === 0 && 'tzMode' in input;\n}\n\n/** Build query for both resolvers and legacy modes */\nfunction getTokenQuery(input: Record<string, string>) {\n return isLegacyModes(input) ? { mode: input.tzMode ?? '.' } : { input };\n}\n\n/** Get the current indent based on the previous line break from char */\nfunction getIndentAtPos(text: string, pos: number) {\n return text.slice(0, pos).match(/\\n(( |\\t)+)$/)?.[1] || '';\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@terrazzo/plugin-tailwind",
|
|
3
|
-
"version": "2.0.0-beta.
|
|
3
|
+
"version": "2.0.0-beta.6",
|
|
4
4
|
"description": "Generate Tailwind v4 theme using DTCG design tokens.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -28,20 +28,21 @@
|
|
|
28
28
|
},
|
|
29
29
|
"peerDependencies": {
|
|
30
30
|
"tailwindcss": "^4.0.0",
|
|
31
|
-
"@terrazzo/cli": "^2.0.0-beta.
|
|
32
|
-
"@terrazzo/parser": "^2.0.0-beta.
|
|
33
|
-
"@terrazzo/plugin-css": "^2.0.0-beta.
|
|
34
|
-
"@terrazzo/token-tools": "^2.0.0-beta.
|
|
31
|
+
"@terrazzo/cli": "^2.0.0-beta.6",
|
|
32
|
+
"@terrazzo/parser": "^2.0.0-beta.6",
|
|
33
|
+
"@terrazzo/plugin-css": "^2.0.0-beta.6",
|
|
34
|
+
"@terrazzo/token-tools": "^2.0.0-beta.6"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@terrazzo/token-tools": "^2.0.0-beta.
|
|
37
|
+
"@terrazzo/token-tools": "^2.0.0-beta.6"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
|
+
"@tailwindcss/cli": "^4.2.0",
|
|
40
41
|
"dtcg-examples": "^1.0.3",
|
|
41
42
|
"tailwindcss": "^4.2.0",
|
|
42
|
-
"@terrazzo/cli": "^2.0.0-beta.
|
|
43
|
-
"@terrazzo/parser": "^2.0.0-beta.
|
|
44
|
-
"@terrazzo/plugin-css": "^2.0.0-beta.
|
|
43
|
+
"@terrazzo/cli": "^2.0.0-beta.6",
|
|
44
|
+
"@terrazzo/parser": "^2.0.0-beta.6",
|
|
45
|
+
"@terrazzo/plugin-css": "^2.0.0-beta.6"
|
|
45
46
|
},
|
|
46
47
|
"scripts": {
|
|
47
48
|
"build": "rolldown -c && attw --profile esm-only --pack .",
|