Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions plugins/postcss-global-data/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changes to PostCSS global-data

### Unreleased (minor)

- Add `prepend` plugin option
- Add `lateRemover` plugin option

### 3.0.0

_August 3, 2024_
Expand Down
44 changes: 43 additions & 1 deletion plugins/postcss-global-data/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ instructions for:

## Options

### files
### `files`

The `files` option determines which files to inject into PostCSS.

Expand All @@ -61,6 +61,48 @@ postcssGlobalData({
});
```

### Plugin order control with `lateRemover`

The `lateRemover` option gives you more options when ordering plugins.

```js
// esm
import postcss from 'postcss';
import postcssGlobalData from '@csstools/postcss-global-data';

const [globalData, globalDataLateRemover] = postcssGlobalData({
files: [
'./src/css/variables.css',
'./src/css/media-queries.css',
],
lateRemover: true
}).plugins

postcss([
globalData,
/* other plugins */
globalDataLateRemover
]).process(YOUR_CSS /*, processOptions */);
```

### `prepend`

The `prepend` option determines if injected CSS is appended or prepended.
Defaults to `false`.

> [!Warning]
> Prepending styles before `@import` statements will create broken stylesheets.

```js
postcssGlobalData({
files: [
'./src/css/variables.css',
'./src/css/media-queries.css',
],
prepend: true
});
```

[cli-url]: https://github.com/csstools/postcss-plugins/actions/workflows/test.yml?query=workflow/test

[discord]: https://discord.gg/bUadyRwkJS
Expand Down
2 changes: 1 addition & 1 deletion plugins/postcss-global-data/dist/index.cjs
Original file line number Diff line number Diff line change
@@ -1 +1 @@
"use strict";var e=require("node:path"),s=require("node:fs"),r=require("node:module");function parseImport(t,o,a,n){let c="";try{if(a.startsWith("node_modules://")){c=r.createRequire(process.cwd()).resolve(a.slice(15))}else if(a.startsWith("node_modules:")){c=r.createRequire(process.cwd()).resolve(a.slice(13))}else c=e.resolve(a)}catch(e){throw new Error(`Failed to read ${a} with error ${e instanceof Error?e.message:e}`)}if(n.has(c))return!1;n.add(c),o.result.messages.push({type:"dependency",plugin:"postcss-global-data",file:c,parent:t.source?.input?.file});const i=s.readFileSync(c,"utf8");return o.postcss.parse(i,{from:c})}const creator=e=>{const s=Object.assign({files:[]},e);return{postcssPlugin:"postcss-global-data",prepare(){let e=new Set,r=new Set;return{postcssPlugin:"postcss-global-data",Once(t,o){s.files.forEach(s=>{if(e.has(s))return;const a=parseImport(t,o,s,e);a&&a.each(e=>{t.append(e),r.add(e)})})},OnceExit(){r.forEach(e=>{e.remove()}),r=new Set,e=new Set}}}}};creator.postcss=!0,module.exports=creator;
"use strict";var e=require("node:path"),r=require("node:fs"),s=require("node:module");function parseImport(t,o,n,c){let a="";try{if(n.startsWith("node_modules://")){a=s.createRequire(process.cwd()).resolve(n.slice(15))}else if(n.startsWith("node_modules:")){a=s.createRequire(process.cwd()).resolve(n.slice(13))}else a=e.resolve(n)}catch(e){throw new Error(`Failed to read ${n} with error ${e instanceof Error?e.message:e}`)}if(c.has(a))return!1;c.add(a),o.result.messages.push({type:"dependency",plugin:"postcss-global-data",file:a,parent:t.source?.input?.file});const i=r.readFileSync(a,"utf8");return o.postcss.parse(i,{from:a})}const t="postcss-global-data",creator=e=>{const r=Object.assign({files:[],lateRemover:!1,prepend:!1},e);function insert(e,s,t){if(!r.prepend)return void s.each(r=>{e.append(r),t.add(r)});const o=Array.from(s.nodes);o.reverse(),o.forEach(r=>{e.prepend(r),t.add(r)})}if(!r.lateRemover)return{postcssPlugin:t,prepare(){const e=new Set,s=new Set;return{postcssPlugin:t,Once(t,o){r.files.forEach(r=>{const n=parseImport(t,o,r,s);n&&insert(t,n,e)})},OnceExit(){e.forEach(e=>{e.remove()}),s.clear(),e.clear()}}}};const s=new WeakSet;return{postcssPlugin:t,plugins:[{postcssPlugin:t,prepare(){const e=new Set;return{postcssPlugin:t,Once(t,o){r.files.forEach(r=>{const n=parseImport(t,o,r,e);n&&insert(t,n,s)})},OnceExit(){e.clear()}}}},{postcssPlugin:t+"/late-remover",OnceExit(e){e.each(e=>{s.has(e)&&e.remove()})}}]}};creator.postcss=!0,module.exports=creator;
4 changes: 4 additions & 0 deletions plugins/postcss-global-data/dist/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ export default creator;
export declare type pluginOptions = {
/** List of files to be used as context */
files?: Array<string>;
/** Remove nodes in a separate plugin object, this object can be added later in your list of plugins */
lateRemover?: boolean;
/** Add global CSS to the start of files, defaults to `false` */
prepend?: boolean;
};

export { }
2 changes: 1 addition & 1 deletion plugins/postcss-global-data/dist/index.mjs
Original file line number Diff line number Diff line change
@@ -1 +1 @@
import e from"node:path";import s from"node:fs";import r from"node:module";function parseImport(t,o,a,n){let c="";try{if(a.startsWith("node_modules://")){c=r.createRequire(process.cwd()).resolve(a.slice(15))}else if(a.startsWith("node_modules:")){c=r.createRequire(process.cwd()).resolve(a.slice(13))}else c=e.resolve(a)}catch(e){throw new Error(`Failed to read ${a} with error ${e instanceof Error?e.message:e}`)}if(n.has(c))return!1;n.add(c),o.result.messages.push({type:"dependency",plugin:"postcss-global-data",file:c,parent:t.source?.input?.file});const l=s.readFileSync(c,"utf8");return o.postcss.parse(l,{from:c})}const creator=e=>{const s=Object.assign({files:[]},e);return{postcssPlugin:"postcss-global-data",prepare(){let e=new Set,r=new Set;return{postcssPlugin:"postcss-global-data",Once(t,o){s.files.forEach(s=>{if(e.has(s))return;const a=parseImport(t,o,s,e);a&&a.each(e=>{t.append(e),r.add(e)})})},OnceExit(){r.forEach(e=>{e.remove()}),r=new Set,e=new Set}}}}};creator.postcss=!0;export{creator as default};
import e from"node:path";import r from"node:fs";import s from"node:module";function parseImport(t,o,n,c){let a="";try{if(n.startsWith("node_modules://")){a=s.createRequire(process.cwd()).resolve(n.slice(15))}else if(n.startsWith("node_modules:")){a=s.createRequire(process.cwd()).resolve(n.slice(13))}else a=e.resolve(n)}catch(e){throw new Error(`Failed to read ${n} with error ${e instanceof Error?e.message:e}`)}if(c.has(a))return!1;c.add(a),o.result.messages.push({type:"dependency",plugin:"postcss-global-data",file:a,parent:t.source?.input?.file});const p=r.readFileSync(a,"utf8");return o.postcss.parse(p,{from:a})}const t="postcss-global-data",creator=e=>{const r=Object.assign({files:[],lateRemover:!1,prepend:!1},e);function insert(e,s,t){if(!r.prepend)return void s.each(r=>{e.append(r),t.add(r)});const o=Array.from(s.nodes);o.reverse(),o.forEach(r=>{e.prepend(r),t.add(r)})}if(!r.lateRemover)return{postcssPlugin:t,prepare(){const e=new Set,s=new Set;return{postcssPlugin:t,Once(t,o){r.files.forEach(r=>{const n=parseImport(t,o,r,s);n&&insert(t,n,e)})},OnceExit(){e.forEach(e=>{e.remove()}),s.clear(),e.clear()}}}};const s=new WeakSet;return{postcssPlugin:t,plugins:[{postcssPlugin:t,prepare(){const e=new Set;return{postcssPlugin:t,Once(t,o){r.files.forEach(r=>{const n=parseImport(t,o,r,e);n&&insert(t,n,s)})},OnceExit(){e.clear()}}}},{postcssPlugin:t+"/late-remover",OnceExit(e){e.each(e=>{s.has(e)&&e.remove()})}}]}};creator.postcss=!0;export{creator as default};
44 changes: 43 additions & 1 deletion plugins/postcss-global-data/docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ can actually use it.

## Options

### files
### `files`

The `files` option determines which files to inject into PostCSS.

Expand All @@ -46,4 +46,46 @@ The `files` option determines which files to inject into PostCSS.
});
```

### Plugin order control with `lateRemover`

The `lateRemover` option gives you more options when ordering plugins.

```js
// esm
import postcss from 'postcss';
import <exportName> from '<packageName>';

const [globalData, globalDataLateRemover] = <exportName>({
files: [
'./src/css/variables.css',
'./src/css/media-queries.css',
],
lateRemover: true
}).plugins

postcss([
globalData,
/* other plugins */
globalDataLateRemover
]).process(YOUR_CSS /*, processOptions */);
```

### `prepend`

The `prepend` option determines if injected CSS is appended or prepended.
Defaults to `false`.

> [!Warning]
> Prepending styles before `@import` statements will create broken stylesheets.

```js
<exportName>({
files: [
'./src/css/variables.css',
'./src/css/media-queries.css',
],
prepend: true
});
```

<linkList>
124 changes: 94 additions & 30 deletions plugins/postcss-global-data/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,57 +1,121 @@
import type { ChildNode, Plugin, PluginCreator } from 'postcss';
import type { ChildNode, Plugin, PluginCreator, Root } from 'postcss';
import { parseImport } from './parse-import';

/** postcss-global-data plugin options */
export type pluginOptions = {
/** List of files to be used as context */
files?: Array<string>,
/** Remove nodes in a separate plugin object, this object can be added later in your list of plugins */
lateRemover?: boolean,
/** Add global CSS to the start of files, defaults to `false` */
prepend?: boolean,
};

const pluginName = 'postcss-global-data';

const creator: PluginCreator<pluginOptions> = (opts?: pluginOptions) => {
const options = Object.assign(
// Default options
{
files: [],
lateRemover: false,
prepend: false,
},
// Provided options
opts,
);

return {
postcssPlugin: 'postcss-global-data',
prepare(): Plugin {
let importedFiles = new Set<string>();
let importedCSS = new Set<ChildNode>();

return {
postcssPlugin: 'postcss-global-data',
Once(root, postcssHelpers): void {
options.files.forEach((file) => {
if (importedFiles.has(file)) {
return;
}
function insert(destination: Root, source: Root, set: { add: (_: ChildNode) => void }): void {
if (!options.prepend) {
source.each((node) => {
destination.append(node);
set.add(node);
});

const newCSS = parseImport(root, postcssHelpers, file, importedFiles);
if (!newCSS) {
return;
}
return;
}

const nodes = Array.from(source.nodes);
nodes.reverse();

newCSS.each((node) => {
root.append(node);
importedCSS.add(node);
nodes.forEach((node) => {
destination.prepend(node);
set.add(node);
});
}

if (!options.lateRemover) {
return {
postcssPlugin: pluginName,
prepare(): Plugin {
const importedCSS = new Set<ChildNode>();
const importedFiles = new Set<string>();

return {
postcssPlugin: pluginName,
Once(root, postcssHelpers): void {
options.files.forEach((file) => {
const newCSS = parseImport(root, postcssHelpers, file, importedFiles);
if (!newCSS) {
return;
}

insert(root, newCSS, importedCSS);
});
});
},
OnceExit(): void {
importedCSS.forEach((node) => {
node.remove();
});

importedFiles.clear();
importedCSS.clear();
},
};
},
}
}

const importedCSS = new WeakSet<ChildNode>();

return {
postcssPlugin: pluginName,
plugins: [
{
postcssPlugin: pluginName,
prepare(): Plugin {
const importedFiles = new Set<string>();

return {
postcssPlugin: pluginName,
Once(root, postcssHelpers): void {
options.files.forEach((file) => {
const newCSS = parseImport(root, postcssHelpers, file, importedFiles);
if (!newCSS) {
return;
}

insert(root, newCSS, importedCSS);
});
},
OnceExit(): void {
importedFiles.clear();
},
};
},
OnceExit(): void {
importedCSS.forEach((node) => {
node.remove();
},
{
postcssPlugin: pluginName + '/late-remover',
OnceExit(root): void {
root.each((node) => {
if (importedCSS.has(node)) {
node.remove();
}
});
importedCSS = new Set<ChildNode>();
importedFiles = new Set<string>();
},
};
},
};
}
]
}
};

creator.postcss = true;
Expand Down
Loading
Loading