fetch-css 5.0.0 → 5.0.2

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/README.md CHANGED
@@ -1,20 +1,22 @@
1
1
  # fetch-css
2
2
  [![](https://img.shields.io/npm/v/fetch-css.svg?style=flat)](https://www.npmjs.org/package/fetch-css) [![](https://img.shields.io/npm/dm/fetch-css.svg)](https://www.npmjs.org/package/fetch-css) [![](https://packagephobia.com/badge?p=fetch-css)](https://packagephobia.com/result?p=fetch-css)
3
+
3
4
  > Extract CSS from websites and browser extensions
4
5
 
5
6
  ## Usage
6
7
 
7
- ```console
8
- npm i fetch-css
8
+ ```sh
9
+ pnpm add fetch-css
9
10
  ```
11
+
10
12
  ```js
11
13
  import fetchCss from "fetch-css";
12
14
 
13
- // retrieve CSS of a website
14
- const [{css}] = await fetchCss([{url: "https://example.com"}]);
15
-
16
- // extract CSS from a Chrome extension
17
- const [{css}] = await fetchCss([{crx: "hlepfoohegkhhmjieoechaddaejaokhf"}]);
15
+ const [website, extension] = await fetchCss([
16
+ {url: "https://example.com"},
17
+ {crx: "hlepfoohegkhhmjieoechaddaejaokhf"}, // a Chrome extension
18
+ ]);
19
+ console.log(website.css, extension.css);
18
20
  ```
19
21
 
20
22
  ## API
@@ -23,12 +25,12 @@ const [{css}] = await fetchCss([{crx: "hlepfoohegkhhmjieoechaddaejaokhf"}]);
23
25
  - `sources`: *Array* Array of source objects
24
26
  - `source`: *Object*
25
27
  - `url`: *string* or *Array* An absolute URL pointing to either a website or directly to a CSS or JS file (to extract inlined CSS strings from)
26
- - `fetchOpts`: *Object* Options passed to [fetch](https://github.com/npm/make-fetch-happen#fetch)
28
+ - `fetchOpts`: *Object* Options passed to [fetch](https://github.com/silverwind/fetch-enhanced#fetchurl-opts)
27
29
  - `crx`: *string* A Chrome extension id
28
- - `contentScriptsOnly`: *boolean* Whether to pull only content scripts from a extension. Default: `false`
30
+ - `contentScriptsOnly`: *boolean* Whether to pull only content scripts from an extension. Default: `false`
29
31
  - `strict`: *boolean* Whether to throw an error if fetch fails. Default: `false`
30
32
 
31
- Returns a `Promise` that resolves to a `sources` array with additional `css` properties present on each source.
33
+ Returns a `Promise` that resolves to a copy of the `sources` array with additional `css` properties present on each source. Sources with an array `url` are expanded into one source per URL.
32
34
 
33
35
  ## Related
34
36
 
package/dist/index.d.ts CHANGED
@@ -22,7 +22,8 @@ export type Source = {
22
22
  /**
23
23
  * Extract CSS from websites and browser extensions.
24
24
  *
25
- * Returns the given `sources` array with an additional `css` property present on each source.
25
+ * Returns a copy of the `sources` array with an additional `css` property present on each source.
26
+ * Sources with an array `url` are expanded into one source per URL.
26
27
  */
27
28
  export default function fetchCss(sources: Array<Source>): Promise<Array<Source>>;
28
29
  //#endregion
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import{parse as e}from"acorn";import t from"rfdc";import n from"postcss";import{parseFragment as r}from"parse5";import i from"unzipper";import a from"url-toolkit";import o from"crypto-js/enc-latin1.js";import s from"crypto-js/sha256.js";import{fetch as c}from"undici";import l from"fetch-enhanced";function u(e,t,n,r){let i=0;return i+=e,i+=t<<8,i+=n<<16,i+=r<<24>>>0,i}function d(e,t,n){let r=``;for(let i=t;i<n;++i)r+=String.fromCharCode(e[i]);return r}function f(e){let t=new Uint8Array(e);if(t[0]===80&&t[1]===75&&t[2]===3&&t[3]===4)throw Error(`Input is not a CRX file, but a ZIP file.`);if(t[0]!==67||t[1]!==114||t[2]!==50||t[3]!==52)throw m(t)?Error(`Input is not a CRX file, but possibly a ZIP file.`):Error(`Invalid header: Does not start with Cr24.`);if(t[4]!==2&&t[4]!==3||t[5]||t[6]||t[7])throw Error(`Unexpected crx format version number.`);let n;if(t[4]===2){let e=u(t[8],t[9],t[10],t[11]),r=u(t[12],t[13],t[14],t[15]);n=16+e+r,btoa(d(t,16,16+e))}else n=12+u(t[8],t[9],t[10],t[11]),p(t,12,n);return e.slice(n)}function p(e,t,n){function r(){let n=e[t]&127;if(e[t++]<128||(n|=(e[t]&127)<<7,e[t++]<128)||(n|=(e[t]&127)<<14,e[t++]<128)||(n|=(e[t]&127)<<21,e[t++]<128))return n;if(n=(n|(e[t]&15)<<28)>>>0,e[t++]&128)throw Error(`proto: not a uint32`);return n}let i=[],a;for(;t<n;){let o=r(),s=r();if(o===80002){let n=r(),i=r();if(n!==10)throw Error(`proto: Unexpected key in signed_header_data: ${n}`);if(i!==16)throw Error(`proto: Unexpected signed_header_data length ${s}`);if(a)throw Error(`proto: Unexpected duplicate signed_header_data`);a=e.subarray(t,t+16),t+=i;continue}if(o!==18){if(o!==26)throw Error(`proto: Unexpected key: ${o}`);t+=s;continue}let c=t+s,l=r(),u=r();if(l===18){if(t+=u,t>=c)continue;l=r(),u=r()}if(l!==10)throw t+=u,Error(`proto: Unexpected key in AsymmetricKeyProof: ${l}`);if(t+u>n)throw Error(`proto: size of public_key field is too large`);i.push(d(e,t,t+u)),t=c}if(!i.length)throw Error(`proto: Did not find any public key`);if(!a)throw Error(`proto: Did not find crx_id`);let c=o.parse(d(a,0,16)).toString();for(let e of i)if(s(o.parse(e)).toString().slice(0,32)===c)return btoa(e);throw Error(`proto: None of the public keys matched with crx_id`)}function m(e){for(let t=e.length-22,n=Math.max(0,t-65535);t>=n;--t)if(e[t]===80&&e[t+1]===75&&e[t+2]===5&&e[t+3]===6)return!0;return!1}const h=l(c,{undici:!0}),g=t();async function _(e,t){try{return await h(e,t)}catch(t){throw t.message=`${t.message} (${e})`,t}}async function v(e){let t=[],n=[],r=await e.text();for(let n of b(r))t.push(a.buildAbsoluteURL(e.url,n));for(let e of x(r))n.push(e);return[t,n]}function y(e,t,n){if(e.status===200)return;let r=`Failed to fetch ${t}: ${e.status} ${e.statusText}`;if(n)throw Error(r);console.warn(`(fetch-css) Warning: ${r}`)}function b(e){return(e.match(/<link.+?>/g)||[]).map(e=>{let t={};for(let n of r(e).childNodes[0].attrs)t[n.name]=n.value;return t.href&&t.rel===`stylesheet`||t.href&&/\.css$/i.test(t.href.replace(/\?.+/,`undefined`))?t.href:null}).filter(Boolean)}function x(e){return Array.from((e||``).matchAll(/<style.*?>([\s\S]*?)<\/style>/g)||[]).map(e=>e[1]).map(e=>e.trim()).filter(Boolean)}function S(e){try{let t=n.parse(e);if(t&&t.type===`root`&&Array.isArray(t.nodes)&&t.nodes.length>=1&&t.nodes.every(e=>e.type===`rule`))return!0}catch{}return!1}function C(e){let t=Buffer.alloc(e.byteLength),n=new Uint8Array(e);for(let e=0;e<t.length;++e)t[e]=n[e];return t}function w(t){let n=``;return e(t,{ecmaVersion:`latest`,onToken:e=>{if(e.type.label===`string`){let t=e.value.trim().replace(/\n/g,``).replace(/^\);\}/,``);t.length>25&&S(t)&&(n+=`${t}\n`)}}}),n.trim()}async function T({crx:e,contentScriptsOnly:t,strict:n}){let r=`https://clients2.google.com/service/update2/crx`;r+=`?response=redirect`,r+=`&os=linux`,r+=`&arch=x86-64`,r+=`&os_arch=x86-64`,r+=`&acceptformat=crx3`,r+=`&prod=chromiumcrx`,r+=`&prodchannel=unknown`,r+=`&prodversion=9999.0.9999.0`,r+=`&x=id%3D${e}`,r+=`%26uc`;let a=await _(r);y(a,r,n);let o=C(await a.arrayBuffer()),s=Buffer.from(f(o)),c={};for(let e of(await i.Open.buffer(s)||{files:[]}).files)c[e.path]=e;if(!c[`manifest.json`])throw Error(`manifest.json not found in extension ${e}`);let l=[],u=[];if(!t)for(let e of Object.keys(c))e.endsWith(`.css`)&&l.push(e),e.endsWith(`.js`)&&u.push(e);let d=JSON.parse(String(await c[`manifest.json`].buffer()));for(let{css:e,js:t}of d.content_scripts||[])Array.isArray(e)&&e.length&&l.push(...e),Array.isArray(t)&&t.length&&u.push(...t);l=Array.from(new Set(l)),u=Array.from(new Set(u)),l=l.map(e=>e.replace(/^\//,``)),u=u.map(e=>e.replace(/^\//,``));let p=``;for(let e of l)p+=`${String(await c[e].buffer())}\n`;for(let e of u){let t=String(await c[e].buffer());p+=w(t)}return p}async function E(e){e=g(e);let t=[];for(let n of e)if(`url`in n&&Array.isArray(n.url))for(let e of n.url)t.push({...n,url:e});else t.push(n);e=t;let n=await Promise.all(e.map(e=>{if(!e.url)return Promise.resolve(null);let{pathname:t}=new URL(e.url);return t.endsWith(`.css`)||t.endsWith(`.js`)?Promise.resolve(null):_(e.url,e.fetchOpts)}));for(let[t,r]of n.entries()){let n=e[t];if(n.urls=[],n.styleTags=[],r){y(r,n.url,n.strict);let[e,t]=await v(r);n.urls.push(...e),n.styleTags.push(...t)}else n.url&&(n.urls=[n.url])}let r=await Promise.all(e.map(e=>e.url?Promise.all(e.urls.map(async e=>(await _(e)).text())):Promise.resolve(null)));for(let[t,n]of r.entries()){let r=e[t];r.crx?r.css=await T(r):r.url.endsWith(`.js`)?r.css=w(n.join(`
2
- `)):(r.css=n.join(`
3
- `),r.styleTags?.length&&(r.css+=`\n${r.styleTags.join(`
4
- `)}`))}return e}export{E as default};
1
+ import{parse as e}from"acorn";import t from"rfdc";import n from"postcss";import{parseFragment as r}from"parse5";import i from"unzipper";import a from"url-toolkit";import{createHash as o}from"node:crypto";import{fetch as s}from"undici";import c from"fetch-enhanced";function l(e,t,n,r){let i=0;return i+=e,i+=t<<8,i+=n<<16,i+=r<<24>>>0,i}function u(e,t,n){let r=``;for(let i=t;i<n;++i)r+=String.fromCharCode(e[i]);return r}function d(e){if(e[0]===80&&e[1]===75&&e[2]===3&&e[3]===4)throw Error(`Input is not a CRX file, but a ZIP file.`);if(e[0]!==67||e[1]!==114||e[2]!==50||e[3]!==52)throw p(e)?Error(`Input is not a CRX file, but possibly a ZIP file.`):Error(`Invalid header: Does not start with Cr24.`);if(e[4]!==2&&e[4]!==3||e[5]||e[6]||e[7])throw Error(`Unexpected crx format version number.`);let t;if(e[4]===2){let n=l(e[8],e[9],e[10],e[11]),r=l(e[12],e[13],e[14],e[15]);t=16+n+r}else t=12+l(e[8],e[9],e[10],e[11]),f(e,12,t);return e.slice(t)}function f(e,t,n){function r(){let n=e[t]&127;if(e[t++]<128||(n|=(e[t]&127)<<7,e[t++]<128)||(n|=(e[t]&127)<<14,e[t++]<128)||(n|=(e[t]&127)<<21,e[t++]<128))return n;if(n=(n|(e[t]&15)<<28)>>>0,e[t++]&128)throw Error(`proto: not a uint32`);return n}let i=[],a;for(;t<n;){let o=r(),s=r();if(o===80002){let n=r(),i=r();if(n!==10)throw Error(`proto: Unexpected key in signed_header_data: ${n}`);if(i!==16)throw Error(`proto: Unexpected signed_header_data length ${s}`);if(a)throw Error(`proto: Unexpected duplicate signed_header_data`);a=e.subarray(t,t+16),t+=i;continue}if(o!==18){if(o!==26)throw Error(`proto: Unexpected key: ${o}`);t+=s;continue}let c=t+s,l=r(),d=r();if(l===18){if(t+=d,t>=c)continue;l=r(),d=r()}if(l!==10)throw t+=d,Error(`proto: Unexpected key in AsymmetricKeyProof: ${l}`);if(t+d>n)throw Error(`proto: size of public_key field is too large`);i.push(u(e,t,t+d)),t=c}if(!i.length)throw Error(`proto: Did not find any public key`);if(!a)throw Error(`proto: Did not find crx_id`);let s=Buffer.from(a).toString(`hex`);for(let e of i)if(o(`sha256`).update(e,`latin1`).digest(`hex`).slice(0,32)===s)return btoa(e);throw Error(`proto: None of the public keys matched with crx_id`)}function p(e){for(let t=e.length-22,n=Math.max(0,t-65535);t>=n;--t)if(e[t]===80&&e[t+1]===75&&e[t+2]===5&&e[t+3]===6)return!0;return!1}const m=c(s,{undici:!0}),h=t();async function g(e,t){try{return await m(e,t)}catch(t){throw t.message=`${t.message} (${e})`,t}}function _(e,t,n){if(e.status===200)return;let r=`Failed to fetch ${t}: ${e.status} ${e.statusText}`;if(n)throw Error(r);console.warn(`(fetch-css) Warning: ${r}`)}function v(e){return(e.match(/<link.+?>/gis)||[]).map(e=>{let t={};for(let n of r(e).childNodes[0].attrs)t[n.name]=n.value;return t.href&&t.rel===`stylesheet`||t.href&&/\.css$/i.test(t.href.replace(/\?.+/,`undefined`))?t.href:null}).filter(Boolean)}function y(e){return Array.from(e.matchAll(/<style.*?>(.*?)<\/style>/gis),e=>e[1].trim()).filter(Boolean)}function b(e){try{let{nodes:t}=n.parse(e);return t.length>=1&&t.every(e=>e.type===`rule`)}catch{return!1}}function x(t,n=`script`){let r=``;try{e(t,{ecmaVersion:`latest`,sourceType:n,onToken:e=>{if(e.type.label===`string`){let t=e.value.trim().replace(/\n/g,``).replace(/^\);\}/,``);t.length>25&&b(t)&&(r+=`${t}\n`)}}})}catch(e){if(n===`module`||!(e instanceof SyntaxError))throw e;try{return x(t,`module`)}catch{throw e}}return r.trim()}async function S({crx:e,contentScriptsOnly:t,strict:n}){let r=`https://clients2.google.com/service/update2/crx`;r+=`?response=redirect`,r+=`&os=linux`,r+=`&arch=x86-64`,r+=`&os_arch=x86-64`,r+=`&acceptformat=crx3`,r+=`&prod=chromiumcrx`,r+=`&prodchannel=unknown`,r+=`&prodversion=9999.0.9999.0`,r+=`&x=id%3D${e}`,r+=`%26uc`;let a=await g(r);_(a,r,n);let o=Buffer.from(d(Buffer.from(await a.arrayBuffer()))),s={};for(let e of(await i.Open.buffer(o)).files)s[e.path]=e;if(!s[`manifest.json`])throw Error(`manifest.json not found in extension ${e}`);let c=[],l=[];if(!t)for(let e of Object.keys(s))e.endsWith(`.css`)&&c.push(e),e.endsWith(`.js`)&&l.push(e);let u=JSON.parse(String(await s[`manifest.json`].buffer()));for(let{css:e,js:t}of u.content_scripts||[])Array.isArray(e)&&c.push(...e),Array.isArray(t)&&l.push(...t);let f=``;for(let e of new Set(c))f+=`${String(await s[e.replace(/^\//,``)].buffer())}\n`;for(let e of new Set(l))f+=x(String(await s[e.replace(/^\//,``)].buffer()));return f}async function C(e){e=h(e).flatMap(e=>`url`in e&&Array.isArray(e.url)?e.url.map(t=>({...e,url:t})):[e]);let t=await Promise.all(e.map(e=>{if(!e.url)return Promise.resolve(null);let{pathname:t}=new URL(e.url);return t.endsWith(`.css`)||t.endsWith(`.js`)?Promise.resolve(null):g(e.url,e.fetchOpts)}));for(let[n,r]of t.entries()){let t=e[n];if(t.urls=[],t.styleTags=[],r){_(r,t.url,t.strict);let e=await r.text();t.urls=v(e).map(e=>a.buildAbsoluteURL(r.url,e)),t.styleTags=y(e)}else t.url&&(t.urls=[t.url])}let n=await Promise.all(e.map(e=>e.url?Promise.all(e.urls.map(async t=>(await g(t,t===e.url?e.fetchOpts:void 0)).text())):Promise.resolve(null)));for(let[t,r]of n.entries()){let n=e[t];n.crx?n.css=await S(n):new URL(n.url).pathname.endsWith(`.js`)?n.css=x(r.join(`
2
+ `)):(n.css=r.join(`
3
+ `),n.styleTags.length&&(n.css+=`\n${n.styleTags.join(`
4
+ `)}`))}return e}export{C as default};
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "fetch-css",
3
- "version": "5.0.0",
3
+ "version": "5.0.2",
4
4
  "description": "Extract CSS from websites and browser extensions",
5
5
  "author": "silverwind",
6
6
  "repository": {
7
7
  "type": "git",
8
- "url": "silverwind/fetch-css"
8
+ "url": "git+https://github.com/silverwind/fetch-css.git"
9
9
  },
10
10
  "license": "BSD-2-Clause",
11
11
  "type": "module",
@@ -29,8 +29,7 @@
29
29
  ],
30
30
  "dependencies": {
31
31
  "acorn": "8.18.0",
32
- "crypto-js": "4.2.0",
33
- "fetch-enhanced": "13.0.0",
32
+ "fetch-enhanced": "13.0.1",
34
33
  "parse5": "8.0.1",
35
34
  "postcss": "8.5.28",
36
35
  "rfdc": "1.4.1",
@@ -39,18 +38,17 @@
39
38
  "url-toolkit": "2.2.5"
40
39
  },
41
40
  "devDependencies": {
42
- "@types/crypto-js": "4.2.2",
43
- "@types/node": "26.6.2",
41
+ "@types/node": "26.6.3",
44
42
  "@types/unzipper": "0.10.11",
45
43
  "@typescript/native-preview": "7.0.0-dev.20260707.2",
46
44
  "eslint": "10.11.0",
47
- "eslint-config-silverwind": "151.0.0",
45
+ "eslint-config-silverwind": "151.0.1",
48
46
  "tsdown": "0.23.0",
49
- "tsdown-config-silverwind": "4.1.1",
47
+ "tsdown-config-silverwind": "4.1.2",
50
48
  "typescript": "6.0.3",
51
- "typescript-config-silverwind": "20.0.4",
52
- "updates": "19.0.0",
53
- "updates-config-silverwind": "4.0.6",
54
- "versions": "15.5.0"
49
+ "typescript-config-silverwind": "21.0.1",
50
+ "updates": "19.0.2",
51
+ "updates-config-silverwind": "4.0.7",
52
+ "versions": "15.5.1"
55
53
  }
56
54
  }