8000 Fix `no-invalid-at-import-rules-when-bundling` and add `pack-test` (#… · csstools/postcss-plugins@2e9fcee · GitHub
Skip to content

Commit 2e9fcee

Browse files
authored
Fix no-invalid-at-import-rules-when-bundling and add pack-test (#1446)
1 parent 56cc454 commit 2e9fcee

File tree

26 files changed

+894
-12
lines changed

26 files changed

+894
-12
lines changed

.github/bin/fail-on-changes.sh

-11
This file was deleted.

package-lock.json

+25
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/pack-test/.gitignore

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
node_modules
2+
package-lock.json
3+
yarn.lock
4+
*.result.css
5+
*.result.code
6+
*.result.log
7+
.*
8+
!.editorconfig
9+
!.gitignore
10+
!.gitkeep
11+
!.rollup.js
12+
!.tape.js

packages/pack-test/CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Changes to Pack Test
2+
3+
### Unreleased (major)
4+
5+
- Initial version

packages/pack-test/LICENSE.md

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
MIT No Attribution (MIT-0)
2+
3+
Copyright © CSSTools Contributors
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy of
6+
this software and associated documentation files (the “Software”), to deal in
7+
the Software without restriction, including without limitation the rights to
8+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
9+
of the Software, and to permit persons to whom the Software is furnished to do
10+
so.
11+
12+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
13+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
14+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
15+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
16+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
17+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
18+
SOFTWARE.

packages/pack-test/README.md

+113
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
# Pack Test
2+
3+
[<img alt="npm version" src="https://img.shields.io/npm/v/@csstools/pack-test.svg" height="20">][npm-url]
4+
[<img alt="Build Status" src="https://github.com/csstools/postcss-plugins/workflows/test/badge.svg" height="20">][cli-url]
5+
6+
Verify that the published contents of your node package will pass a basic smoke test.
7+
This tests creates a `pack` of your node modules just like `npm publish` and tries to import it.
8+
9+
> Why do static analysis when you can brute force it?
10+
11+
## API
12+
13+
[Read the API docs](./docs/pack-test.md)
14+
15+
## Usage
16+
17+
See [`test/_tape.mjs`](https://github.com/csstools/postcss-plugins/blob/main/plugins/postcss-base-plugin/test/_tape.mjs) in the base plugin for a minimal example.
18+
19+
1. Install this package as a dev dependency.
20+
21+
```bash
22+
npm install @csstools/pack-test --save-dev
23+
```
24+
25+
2. Create a `test` directory in your project.
26+
27+
3. Write some CSS that will be processed by your plugin.
28+
29+
```css
30+
/* test/basic.css */
31+
.foo {
32+
color: oklch(40% 0.268735435 34.568626);
33+
}
34+
```
35+
36+
4. Describe your test cases in a JavaScript file.
37+
38+
```js
39+
/* test/_tape.mjs */
40+
import { postcssTape } from '@csstools/pack-test';
41+
import plugin from '<your plugin package name>';
42+
43+
postcssTape(plugin)({
44+
basic: {
45+
message: "supports basic usage",
46+
},
47+
'basic:color': {
48+
message: "supports { color: '<a color>' }",
49+
options: {
50+
color: 'purple'
51+
}
52+
},
53+
});
54+
```
55+
56+
5. Run the tests.
57+
58+
```sh
59+
# See https://nodejs.org/docs/latest/api/test.html for more usage details.
60+
node --test
61+
```
62+
63+
```json
64+
{
65+
"scripts": {
66+
"test": "node --test"
67+
}
68+
}
69+
```
70+
71+
Browse the [source code and tests here](https://github.com/csstools/postcss-plugins/tree/main/packages/pack-test) or see [tests in plugins](https://github.com/csstools/postcss-plugins/tree/main/plugins) for more usage details.
72+
73+
> [!NOTE]
74+
> We use `test/_tape.mjs` for our tests, but you can use any file name you want.
75+
> We like to group things in a `test` directory and we use a leading underscore to sort it before the css files.
76+
77+
## File name format and test case naming
78+
79+
Test source files and test case names are related.
80+
The test case name is expected to be the relative file path with the `.css` extension removed.
81+
82+
Test variants (with different plugin options) are separated by a colon `:`.
83+
84+
All test files are expected to be in a `test` directory in the current working directory.
85+
86+
| test case name | file name | result file name | notes |
87+
| --- | --- | --- | --- |
88+
| `basic` | `test/basic.css` | `test/basic.result.css` | |
89+
| `basic:color` | `test/basic.css` | `test/basic.color.result.css` | A variant test for `basic`. Everything after `:` is ignored. |
90+
| `foo/bar` | `test/foo/bar.css` | `test/foo/bar.result.css` | A test file in a directory |
91+
92+
## `.gitignore`
93+
94+
We recommend adding `*.result.css` to your `.gitignore` file.
95+
This is not functionally required, but it will reduce noise in your git history.
96+
97+
## Quickly update all `expect.css` files.
98+
99+
Set env variable `REWRITE_EXPECTS` to `true` to update all `expect.css` files.
100+
101+
example :
102+
103+
```json
104+
{
105+
"scripts": {
106+
"test": "node --test",
107+
"test:rewrite-expects": "REWRITE_EXPECTS=true node --test"
108+
}
109+
}
110+
```
111+
112+
[cli-url]: https://github.com/csstools/postcss-plugins/actions/workflows/test.yml?query=workflow/test
113+
[npm-url]: https://www.npmjs.com/package/@csstools/pack-test

packages/pack-test/api-extractor.json

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
3+
"extends": "../../api-extractor.json",
4+
"docModel": {
5+
"enabled": true
6+
}
7+
}

packages/pack-test/dist/index.cjs

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"use strict";var e=require("node:url"),n=require("node:fs/promises"),t=require("node:path"),i=require("node:os"),o=require("node:process"),a=require("node:child_process"),r="undefined"!=typeof document?document.currentScript:null;const s="package";async function findPackageJsonFromDir(e,i=10){const o=t.join(e,"package.json");try{return await n.access(o),o}catch{if("/"===e||i<=0)throw new Error("Could not find package.json")}return findPackageJsonFromDir(t.dirname(e),i-1)}async function pack(e,i){const r=await n.mkdir(t.join(i,"pack"),{recursive:!0}),s=a.spawn("npm",["pack","--pack-destination",r],{cwd:e,shell:"win32"===o.platform}),c=await new Promise(((e,n)=>{let t="",i="";s.stdout.on("data",(e=>{t+=e.toString()})),s.stderr.on("data",(e=>{i+=e.toString()})),s.on("close",(o=>{0===o?e(t.trim()):(console.error(i),n(new Error(`npm pack exited with code ${o}`)))}))}));return t.join(r,c)}async function unpack(e,i){const o=t.join(i,s);await n.mkdir(o,{recursive:!0});const r=a.spawn("tar",["-xf",e,"-C",s,"--strip-components","1"],{cwd:i});return await new Promise(((e,n)=>{r.on("close",(t=>{0===t?e():n(new Error(`tar exited with code ${t}`))}))})),o}async function eraseDevDependenciesInfo(e){const t=JSON.parse(await n.readFile(e,"utf8"));delete t.devDependencies,await n.writeFile(e,JSON.stringify(t,null,"\t"))}async function getPackageInfo(e){return JSON.parse(await n.readFile(e,"utf8"))}async function createRootPackage(e,i){await n.writeFile(t.join(e,"package.json"),JSON.stringify({name:"@csstools/pack-test--root",private:!0,type:"module",version:"1.0.0",description:"",workspaces:[s],dependencies:i.peerDependencies??{},scripts:{test:"node --test"}},null,"\t")),await n.writeFile(t.join(e,"index.mjs"),`import '${i.name}';`)}async function runNPMInstall(e){const n=a.spawn("npm",["install","--omit","dev"],{cwd:e,stdio:"inherit",shell:"win32"===o.platform});await new Promise(((e,t)=>{n.on("close",(n=>{0===n?e():t(new Error(`npm install exited with code ${n}`))}))}))}async function runTest(e){const n=a.spawn("node",["index.mjs"],{cwd:e,stdio:"inherit",shell:"win32"===o.platform});await new Promise(((e,t)=>{n.on("close",(n=>{0===n?e():t(new Error(`npm install exited with code ${n}`))}))}))}exports.testPack=async function testPack(a){if(o.platform.startsWith("win"))return void console.log("Skipping test on Windows");if(!("resolve"in{url:"undefined"==typeof document?require("url").pathToFileURL(__filename).href:r&&r.src||new URL("index.cjs",document.baseURI).href}))return void console.log("Skipping test on platform without `import.meta.resolve` support");const s=await n.mkdtemp(t.join(i.tmpdir(),"csstools-pack-test-"));let c=!1;try{const n=new URL((void 0)(a));console.log(`Testing module: ${a}`);const i=e.fileURLToPath(n),o=await findPackageJsonFromDir(t.dirname(i)),r=t.dirname(o),c=await pack(r,s),d=await unpack(c,s),p=await getPackageInfo(t.join(d,"package.json"));await eraseDevDependenciesInfo(t.join(d,"package.json")),await createRootPackage(s,p),await runNPMInstall(s),await runTest(s)}catch(e){console.error(e),c=!0}finally{await n.rm(s,{recursive:!0})}c&&process.exit(1)};

packages/pack-test/dist/index.d.ts

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/**
2+
* Verify that the published contents of your node package will pass a basic smoke test.
3+
*
4+
* @example
5+
* ```sh
6+
* node --test
7+
* ```
8+
*
9+
* ```js
10+
* // test/pack.test.mjs
11+
* import { testPack } from '@csstools/pack-test';
12+
*
13+
* await testPack("your-module-name");
14+
* ```
15+
*
16+
* @packageDocumentation
17+
*/
18+
19+
export declare function testPack(moduleName: string): Promise<void>;
20+
21+
export { }

packages/pack-test/dist/index.mjs

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
import e from"node:url";import n from"node:fs/promises";import t from"node:path";import o from"node:os";import{platform as i}from"node:process";import{spawn as a}from"node:child_process";const r="package";async function testPack(a){if(i.startsWith("win"))return void console.log("Skipping test on Windows");if(!("resolve"in import.meta))return void console.log("Skipping test on platform without `import.meta.resolve` support");const r=await n.mkdtemp(t.join(o.tmpdir(),"csstools-pack-test-"));let s=!1;try{const n=new URL(import.meta.resolve(a));console.log(`Testing module: ${a}`);const o=e.fileURLToPath(n),i=await findPackageJsonFromDir(t.dirname(o)),s=t.dirname(i),c=await pack(s,r),p=await unpack(c,r),d=await getPackageInfo(t.join(p,"package.json"));await eraseDevDependenciesInfo(t.join(p,"package.json")),await createRootPackage(r,d),await runNPMInstall(r),await runTest(r)}catch(e){console.error(e),s=!0}finally{await n.rm(r,{recursive:!0})}s&&process.exit(1)}async function findPackageJsonFromDir(e,o=10){const i=t.join(e,"package.json");try{return await n.access(i),i}catch{if("/"===e||o<=0)throw new Error("Could not find package.json")}return findPackageJsonFromDir(t.dirname(e),o-1)}async function pack(e,o){const r=await n.mkdir(t.join(o,"pack"),{recursive:!0}),s=a("npm",["pack","--pack-destination",r],{cwd:e,shell:"win32"===i}),c=await new Promise(((e,n)=>{let t="",o="";s.stdout.on("data",(e=>{t+=e.toString()})),s.stderr.on("data",(e=>{o+=e.toString()})),s.on("close",(i=>{0===i?e(t.trim()):(console.error(o),n(new Error(`npm pack exited with code ${i}`)))}))}));return t.join(r,c)}async function unpack(e,o){const i=t.join(o,r);await n.mkdir(i,{recursive:!0});const s=a("tar",["-xf",e,"-C",r,"--strip-components","1"],{cwd:o});return await new Promise(((e,n)=>{s.on("close",(t=>{0===t?e():n(new Error(`tar exited with code ${t}`))}))})),i}async function eraseDevDependenciesInfo(e){const t=JSON.parse(await n.readFile(e,"utf8"));delete t.devDependencies,await n.writeFile(e,JSON.stringify(t,null,"\t"))}async function getPackageInfo(e){return JSON.parse(await n.readFile(e,"utf8"))}async function createRootPackage(e,o){await n.writeFile(t.join(e,"package.json"),JSON.stringify({name:"@csstools/pack-test--root",private:!0,type:"module",version:"1.0.0",description:"",workspaces:[r],dependencies:o.peerDependencies??{},scripts:{test:"node --test"}},null,"\t")),await n.writeFile(t.join(e,"index.mjs"),`import '${o.name}';`)}async function runNPMInstall(e){const n=a("npm",["install","--omit","dev"],{cwd:e,stdio:"inherit",shell:"win32"===i});await new Promise(((e,t)=>{n.on("close",(n=>{0===n?e():t(new Error(`npm install exited with code ${n}`))}))}))}async function runTest(e){const n=a("node",["index.mjs"],{cwd:e,stdio:"inherit",shell:"win32"===i});await new Promise(((e,t)=>{n.on("close",(n=>{0===n?e():t(new Error(`npm install exited with code ${n}`))}))}))}export{testPack};

packages/pack-test/docs/index.md

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
2+
3+
[Home](./index.md)
4+
5+
## API Reference
6+
7+
## Packages
8+
9+
<table><thead><tr><th>
10+
11+
Package
12+
13+
14+
</th><th>
15+
16+
Description
17+
18+
19+
</th></tr></thead>
20+
<tbody><tr><td>
21+
22+
[@csstools/pack-test](./pack-test.md)
23+
24+
25+
</td><td>
26+
27+
Verify that the published contents of your node package will pass a basic smoke test.
28+
29+
30+
</td></tr>
31+
</tbody></table>

0 commit comments

Comments
 (0)