Skip to content
This repository has been archived by the owner on Aug 1, 2021. It is now read-only.

Commit

Permalink
Merge pull request #3 from jarrodldavis/postcss-config
Browse files Browse the repository at this point in the history
Allow using PostCSS config
  • Loading branch information
jarrodldavis committed Nov 2, 2020
2 parents e602494 + 5cfa625 commit 8151bf8
Show file tree
Hide file tree
Showing 167 changed files with 689 additions and 101 deletions.
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
dist
node_modules
tests/fixtures
5 changes: 4 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@
},
"rules": {
"no-process-exit": "off",
"node/no-missing-import": ["error", { "tryExtensions": [".js", ".ts"], "allowModules": ["estree", "estree-jsx"] }],
"node/no-missing-import": [
"error",
{ "tryExtensions": [".js", ".ts"], "allowModules": ["estree", "estree-jsx", "json-schema"] }
],
"node/shebang": ["error", { "convertPath": { "lib/**/*.ts": ["^lib/(.+?)\\.ts$", "dist/lib/$1.js"] } }]
}
}
3 changes: 1 addition & 2 deletions .mocharc.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
{
"spec": "dist/tests/**/*.test.js",
"slow": 150,
"require": "./dist/tests/prime-class-cache.js"
"slow": 125
}
2 changes: 1 addition & 1 deletion lib/rules/no-unknown-class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export const meta: Metadata = {

export const create: Create = function create(context) {
const options = getOptions(context.options);
const classes = getClasses(options);
const classes = getClasses(options, context.getCwd());

const reporter = new ReportDescriptorBuilder(context);

Expand Down
58 changes: 51 additions & 7 deletions lib/util/extract-classes-bin.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,55 @@
import type { JSONSchema4 } from "json-schema";
import type { Root, Transformer } from "postcss";

import path from "path";

import Ajv from "ajv";
import postcss from "postcss";
import postcssrc from "postcss-load-config";
import createSelectorParser from "postcss-selector-parser";
import tailwindcss from "tailwindcss";

export interface ExtractArgs {
cwd: string;
styles: string;
stylesPath: string | null;
configPath: string | null;
config: { postcss: true } | { tailwind: string | null };
}

const EXTRACT_ARGS_SCHEMA: JSONSchema4 = {
type: "object",
properties: {
cwd: { type: "string" },
styles: { type: "string" },
stylesPath: { type: ["string", "null"] },
config: {
type: "object",
oneOf: [
{
properties: {
postcss: { type: "boolean", enum: [true] },
},
additionalProperties: false,
required: ["postcss"],
},
{
properties: {
tailwind: { type: ["string", "null"] },
},
additionalProperties: false,
required: ["tailwind"],
},
],
},
},
additionalProperties: false,
required: ["cwd", "styles", "stylesPath", "config"],
};

const ajv = new Ajv();

export default async function extractClasses(args: ExtractArgs): Promise<Iterable<string>> {
const { styles, stylesPath, configPath } = args;
const { cwd, styles, stylesPath, config } = args;

const classes = new Set<string>();

Expand All @@ -27,9 +65,15 @@ export default async function extractClasses(args: ExtractArgs): Promise<Iterabl
});
};

const config = configPath ?? undefined;
const from = stylesPath ?? undefined;
await postcss(tailwindcss(config), transformer).process(styles, { from });
const from = stylesPath ? path.resolve(cwd, stylesPath) : undefined;

if ("tailwind" in config) {
const configPath = config.tailwind ? path.resolve(cwd, config.tailwind) : undefined;
await postcss(tailwindcss(configPath), transformer).process(styles, { from });
} else {
const { options, plugins } = await postcssrc({ cwd, from }, cwd);
await postcss([...plugins, transformer]).process(styles, { from, ...options });
}

return classes;
}
Expand All @@ -50,8 +94,8 @@ async function getArgs(): Promise<ExtractArgs> {
throw new Error("Expected object");
}

if (!("styles" in parsedArgs) || !("stylesPath" in parsedArgs)) {
throw new Error("Missing styles or stylesPath");
if (!ajv.validate(EXTRACT_ARGS_SCHEMA, parsedArgs)) {
throw new Error(ajv.errorsText());
}

return parsedArgs as ExtractArgs;
Expand Down
32 changes: 19 additions & 13 deletions lib/util/get-classes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,29 @@ import { createHash } from "crypto";
import fs from "fs";
import path from "path";

function readStyles(stylesPath: string | null): [string, string | null] {
// language=PostCSS
const DEFAULT_INPUT_STYLE_SHEET = `
@tailwind base;
@tailwind utilities;
@tailwind components;
`;
const DEFAULT_INPUT_STYLE_SHEET = `
@tailwind base;
@tailwind utilities;
@tailwind components;
`;

const DEFAULT_INPUT_CHANGED = new Date();

function readStyles(cwd: string, stylesPath: string | null): [string, string | null, Date] {
if (stylesPath) {
return [fs.readFileSync(stylesPath, "utf-8"), path.resolve(stylesPath)];
const fullStylesPath = path.resolve(cwd, stylesPath);
const { mtime } = fs.statSync(fullStylesPath);
return [fs.readFileSync(fullStylesPath, "utf-8"), fullStylesPath, mtime];
} else {
return [DEFAULT_INPUT_STYLE_SHEET, null];
return [DEFAULT_INPUT_STYLE_SHEET, null, DEFAULT_INPUT_CHANGED];
}
}

function extractClasses(args: ExtractArgs): string[] {
const rawClasses = execFileSync(process.argv[0], [path.join(__dirname, "./extract-classes-bin.js")], {
encoding: "utf-8",
input: JSON.stringify(args),
stdio: "pipe",
});

const parsedClasses = JSON.parse(rawClasses) as unknown;
Expand All @@ -36,16 +40,18 @@ function extractClasses(args: ExtractArgs): string[] {
}

let cachedStylesHash = "";
let cachedStylesChanged: Date = new Date();
let cachedClassesValue = new Set<string>();

export default function getClasses(options: Options): Set<string> {
const [styles, stylesPath] = readStyles(options.stylesheet);
export default function getClasses(options: Options, cwd: string): Set<string> {
const [styles, stylesPath, stylesChanged] = readStyles(cwd, options.stylesheet);

const stylesHash = createHash("sha1").update(styles).digest("hex");

if (stylesHash !== cachedStylesHash) {
const classes = extractClasses({ styles, stylesPath, configPath: options.config });
if (stylesHash !== cachedStylesHash || stylesChanged.valueOf() !== cachedStylesChanged.valueOf()) {
const classes = extractClasses({ cwd, styles, stylesPath, config: options.config });
cachedStylesHash = stylesHash;
cachedStylesChanged = stylesChanged;
cachedClassesValue = new Set(classes);
}

Expand Down
34 changes: 29 additions & 5 deletions lib/util/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ import type { Rule } from "eslint";
type Schema = Rule.RuleMetaData["schema"];

export interface Options {
config: string | null;
config: { postcss: true } | { tailwind: string | null };
stylesheet: string | null;
classNameAttributes: string[];
classNameBuilders: string[];
}

const DEFAULT_OPTIONS: Options = {
config: null,
config: { tailwind: null },
stylesheet: null,
classNameAttributes: ["className", "class"],
classNameBuilders: ["clsx", "classcat", "classnames", "classNames"],
Expand All @@ -23,9 +23,33 @@ export const schema: Schema = [
type: "object",
definitions: {
config: {
title: "Tailwind Configuration",
description: "The path to the Tailwind configuration file, relative to the current working directory.",
type: "string",
title: "Configuration",
description: "PostCSS or Tailwind configuration.",
type: "object",
oneOf: [
{
properties: {
postcss: {
title: "PostCSS Configuration",
description: "Use additional PostCSS plugins declared in postcss.config.js (or similar configuration).",
type: "boolean",
enum: [true],
},
},
additionalProperties: false,
required: ["postcss"],
},
{
properties: {
tailwind: {
title: "Tailwind Configuration",
description: "The path to the Tailwind configuration file, relative to the current working directory.",
type: "string",
},
},
additionalProperties: false,
},
],
default: DEFAULT_OPTIONS.config,
},
stylesheet: {
Expand Down

0 comments on commit 8151bf8

Please sign in to comment.