@altano/html-cdnify 2.0.0 → 3.0.0

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,3 +1,5 @@
1
+ # html-cdnify
2
+
1
3
  ![logo](./assets/logo.png)
2
4
 
3
5
  [![npm](https://badgen.net/npm/v/@altano/html-cdnify)](https://www.npmjs.com/package/@altano/html-cdnify) ![Typed with TypeScript](https://badgen.net/npm/types/@altano/html-cdnify) ![ESM only](https://badgen.net/badge/module/esm%20only?icon=js)
@@ -15,7 +17,7 @@ Transform the relative URLs in your HTML markup (e.g. scripts, stylesheets, imag
15
17
  # Simplest usage
16
18
 
17
19
  ```js
18
- var cdnify = require("html-cdnify").cdnify;
20
+ import { cdnify } from "@altano/html-cdnify");
19
21
 
20
22
  cdnify({
21
23
  cdnUrl: "//cdn.com",
@@ -66,7 +68,7 @@ Adding the `data-cdn-ignore` attribute to any element will cause the element to
66
68
  You can specify `bufferPath`, the relative path to the buffer being processed. If this isn't specified, all resources with relative paths will be assumed to be at the root of the domain before being cdnified.
67
69
 
68
70
  ```js
69
- var cdnify = require("html-cdnify").cdnify;
71
+ import { cdnify } from "@altano/html-cdnify");
70
72
 
71
73
  var input = `
72
74
  <img src="figure1.png">
@@ -100,7 +102,7 @@ _Without_ `bufferPath`, the output would be:
100
102
  If your CDN has a path specified, it is assumed that both relative URLs AND root-relative URLs in your HTML are relative to the CDN directory. In other words, the CDN subdirectory will always be in the final URL. For example:
101
103
 
102
104
  ```js
103
- var cdnify = require("html-cdnify").cdnify;
105
+ import { cdnify } from "@altano/html-cdnify");
104
106
 
105
107
  var input = `
106
108
  <img src="figure1.png">
@@ -129,8 +131,8 @@ It should usually be the expectation that a CDN subdir is always present in the
129
131
  If you are dealing with large files or network IO and would like to use the underlying NodeJS Transform Stream rather than buffering all of the input and output, feel free:
130
132
 
131
133
  ```js
132
- var streamifier = require("streamifier");
133
- var CDNTransformer = require("html-cdnify").CDNTransformer;
134
+ import streamifier from "streamifier";
135
+ import { CDNTransformer } from "@altano/html-cdnify");
134
136
 
135
137
  var transformer = new CDNTransformer({
136
138
  cdnUrl: "http://cdn.com",
@@ -155,7 +157,7 @@ outputStream.pipe(process.stdout);
155
157
  ## Specifying a transform defintion to add `<custom-element src="____">` to the list of attributes to be cdnified
156
158
 
157
159
  ```js
158
- var cdnify = require("html-cdnify").cdnify;
160
+ import { cdnify } from "@altano/html-cdnify");
159
161
 
160
162
  var outputPromise = cdnify({
161
163
  cdnUrl: "//cdn.com/cdn/",
@@ -177,7 +179,7 @@ outputPromise.then((output) => console.log(output));
177
179
  ## Specifying a custom transform function
178
180
 
179
181
  ```js
180
- var cdnify = require("html-cdnify").cdnify;
182
+ import { cdnify } from "@altano/html-cdnify");
181
183
 
182
184
  var outputPromise = cdnify({
183
185
  cdnUrl: "//cdn.com",
@@ -198,8 +200,7 @@ outputPromise.then((output) => console.log(output));
198
200
  If your assets are on different CDNs, you might need to write something like:
199
201
 
200
202
  ```js
201
- var cdnify = require("html-cdnify").cdnify;
202
- var CDNTransformer = require("html-cdnify").CDNTransformer;
203
+ import { cdnify, CDNTransformer } from "@altano/html-cdnify");
203
204
 
204
205
  var outputPromise = cdnify({
205
206
  cdnUrl: "//cdn.com",
@@ -225,7 +226,7 @@ outputPromise.then((output) => console.log(output));
225
226
  A custom attributeParser is like a custom transformFunction, but is scoped to just one selector rather than being applied to every attribute transformation. For example, to uppercase only PNG image names before cdnifying:
226
227
 
227
228
  ```js
228
- var cdnify = require("html-cdnify").cdnify;
229
+ import { cdnify } from "@altano/html-cdnify");
229
230
 
230
231
  var outputPromise = cdnify({
231
232
  cdnUrl: "//cdn.com",
@@ -303,7 +304,7 @@ The default transformDefinitions array is:
303
304
  If you'd like to ovverride any of these, specify the same selector and attribute but a different attributeParser. For example:
304
305
 
305
306
  ```js
306
- var cdnify = require("html-cdnify").cdnify;
307
+ import { cdnify } from "@altano/html-cdnify");
307
308
 
308
309
  var noopFn = (oldAttribute) => oldAttribute;
309
310
 
@@ -330,7 +331,7 @@ outputPromise.then((output) => console.log(output));
330
331
  ## Promise-based API (recommended for simplicity)
331
332
 
332
333
  ```js
333
- var cdnify = require("html-cdnify").cdnify;
334
+ import { cdnify } from "@altano/html-cdnify");
334
335
 
335
336
  cdnify({ ... Options ...}) => Promise<Buffer>
336
337
  ```
@@ -348,7 +349,7 @@ Same options as stream-based API (see below), plus:
348
349
  use the stream-based API when you are dealing with large files or network IO and would like to use the underlying NodeJS Transform Stream rather than buffering all of the input and output. The Promisebased API is simpler but the input/output must be completely buffered, which is obviously fine most of the time.
349
350
 
350
351
  ```js
351
- var CDNTransformer = require("html-cdnify").CDNTransformer;
352
+ import { CDNTransformer } from "@altano/html-cdnify");
352
353
 
353
354
  var transformer = new CDNTransformer({ ... Options ... }) => CDNTransformer
354
355
  transformer.stream => NodeJS TransformStream
@@ -1,12 +1,9 @@
1
- import { TransformDefinition, HtmlAttributeStreamTransformer } from './HtmlAttributeStreamTransformer.js';
2
- import './HtmlTransformer.js';
3
- import '@gofunky/trumpet';
4
-
5
- type CDNTransformFunction = (cdnUrl: string, oldUrl: string, bufferPath: string) => string;
6
- interface CDNTransformerOptions {
1
+ import * as HtmlAttributeStreamTransformer from "./HtmlAttributeStreamTransformer";
2
+ export type CDNTransformFunction = (cdnUrl: string, oldUrl: string, bufferPath: string) => string;
3
+ export interface CDNTransformerOptions {
7
4
  cdnUrl: string;
8
5
  bufferPath?: string;
9
- transformDefinitions?: TransformDefinition[];
6
+ transformDefinitions?: HtmlAttributeStreamTransformer.TransformDefinition[];
10
7
  transformFunction?: CDNTransformFunction;
11
8
  }
12
9
  /**
@@ -15,11 +12,10 @@ interface CDNTransformerOptions {
15
12
  * attributes that identify CDN-able resources converted to
16
13
  * use the given CDN URL.
17
14
  */
18
- declare class CDNTransformer extends HtmlAttributeStreamTransformer {
15
+ export declare class CDNTransformer extends HtmlAttributeStreamTransformer.HtmlAttributeStreamTransformer {
19
16
  #private;
20
17
  cdnOptions: Required<CDNTransformerOptions>;
21
18
  constructor(cdnOptions: CDNTransformerOptions);
22
19
  static defaultTransformFunction: (newUrlBase: string, oldUrl: string, pathOldUrlIsRelativeTo?: string | undefined) => string;
23
20
  }
24
-
25
- export { type CDNTransformFunction, CDNTransformer, type CDNTransformerOptions };
21
+ //# sourceMappingURL=CDNTransformer.d.ts.map
@@ -1,9 +1,125 @@
1
- import {
2
- CDNTransformer
3
- } from "./chunk-YUWY5QAX.js";
4
- import "./chunk-UFAXQSS4.js";
5
- import "./chunk-DUQPDAQ6.js";
6
- import "./chunk-ZHBDQZLT.js";
1
+ import { unionBy, merge, mergeWith } from "lodash";
2
+ import urlConverter from "./urlConverter";
3
+ import * as HtmlAttributeStreamTransformer from "./HtmlAttributeStreamTransformer";
4
+ class OptionsMerger {
5
+ static isTransformDefinition(a) {
6
+ return a != null && typeof a === "object" && "selector" in a && "attribute" in a && typeof a.selector === "string" && typeof a.selector === "string" && typeof a.attribute === "string";
7
+ }
8
+ static isTransformDefinitions(a) {
9
+ return Array.isArray(a) && a.length > 0 && OptionsMerger.isTransformDefinition(a[0]);
10
+ }
11
+ static mergeCustomizer = (objValue, srcValue) => {
12
+ if (OptionsMerger.isTransformDefinitions(objValue) || OptionsMerger.isTransformDefinitions(srcValue)) {
13
+ const result = unionBy(
14
+ srcValue,
15
+ objValue,
16
+ // reverse order to allow srcValue to take precedence
17
+ (value) => `${value.selector} / ${value.attribute}`
18
+ );
19
+ return result;
20
+ } else {
21
+ return void 0;
22
+ }
23
+ };
24
+ static merge(target, source1, source2, source3) {
25
+ return mergeWith(
26
+ target,
27
+ source1,
28
+ source2,
29
+ source3,
30
+ OptionsMerger.mergeCustomizer
31
+ );
32
+ }
33
+ }
34
+ class CDNTransformer extends HtmlAttributeStreamTransformer.HtmlAttributeStreamTransformer {
35
+ cdnOptions;
36
+ constructor(cdnOptions) {
37
+ super(
38
+ OptionsMerger.merge(
39
+ {},
40
+ CDNTransformer.#defaultHtmlAttributeStreamTransformerOptions,
41
+ {
42
+ transformDefinitions: [],
43
+ transformFunction: (attribute) => this.#cdnifyAttribute(attribute)
44
+ },
45
+ {
46
+ transformDefinitions: cdnOptions.transformDefinitions
47
+ }
48
+ )
49
+ );
50
+ this.cdnOptions = merge(
51
+ {
52
+ transformDefinitions: []
53
+ },
54
+ CDNTransformer.#defaultCdnOptions,
55
+ cdnOptions
56
+ );
57
+ if (typeof this.cdnOptions.cdnUrl !== "string") {
58
+ throw new Error(`Invalid cdnUrl "${this.cdnOptions.cdnUrl}" specified.`);
59
+ }
60
+ }
61
+ #cdnifyAttribute(oldUrl) {
62
+ return this.cdnOptions.transformFunction(
63
+ this.cdnOptions.cdnUrl,
64
+ oldUrl,
65
+ this.cdnOptions.bufferPath
66
+ );
67
+ }
68
+ static #defaultCdnOptions = {
69
+ bufferPath: ".",
70
+ transformFunction: urlConverter
71
+ };
72
+ static defaultTransformFunction = CDNTransformer.#defaultCdnOptions.transformFunction;
73
+ static #defaultHtmlAttributeStreamTransformerOptions = {
74
+ attributeToMarkElementToBeIgnored: "data-cdn-ignore",
75
+ // inspired by https://github.com/callumlocke/grunt-cdnify/blob/master/tasks/cdnify.js#L31
76
+ transformDefinitions: [
77
+ {
78
+ selector: `video[poster]:not([data-cdn-ignore])`,
79
+ attribute: "poster"
80
+ },
81
+ {
82
+ selector: `img[data-src]:not([data-cdn-ignore])`,
83
+ attribute: "data-src"
84
+ },
85
+ {
86
+ selector: `script[src]:not([data-cdn-ignore])`,
87
+ attribute: "src"
88
+ },
89
+ {
90
+ selector: `source[src]:not([data-cdn-ignore])`,
91
+ attribute: "src"
92
+ },
93
+ {
94
+ selector: `link[rel="apple-touch-icon"]:not([data-cdn-ignore])`,
95
+ attribute: "href"
96
+ },
97
+ {
98
+ selector: `link[rel="icon"]:not([data-cdn-ignore])`,
99
+ attribute: "href"
100
+ },
101
+ {
102
+ selector: `link[rel="shortcut icon"]:not([data-cdn-ignore])`,
103
+ attribute: "href"
104
+ },
105
+ {
106
+ selector: `link[rel="stylesheet"]:not([data-cdn-ignore])`,
107
+ attribute: "href"
108
+ },
109
+ {
110
+ selector: `img[src]:not([data-cdn-ignore])`,
111
+ attribute: "src"
112
+ },
113
+ {
114
+ selector: `img[srcset]:not([data-cdn-ignore])`,
115
+ attribute: "srcset",
116
+ attributeParser(attr, transformFunction) {
117
+ return attr.split(",").map((imgInfo) => imgInfo.replace(/([^ ]+)/, transformFunction)).join(",");
118
+ }
119
+ }
120
+ ]
121
+ };
122
+ }
7
123
  export {
8
124
  CDNTransformer
9
125
  };
@@ -1,21 +1,19 @@
1
- import HtmlTransformer from './HtmlTransformer.js';
2
- import '@gofunky/trumpet';
3
-
4
- interface HtmlAttributeStreamTransformerOptions {
1
+ import HtmlTransformer from "./HtmlTransformer";
2
+ export interface HtmlAttributeStreamTransformerOptions {
5
3
  transformDefinitions: TransformDefinition[];
6
4
  transformFunction: TransformFunction;
7
5
  attributeToMarkElementToBeIgnored?: string;
8
6
  }
9
- interface TransformDefinition {
7
+ export interface TransformDefinition {
10
8
  selector: string;
11
9
  attribute: string;
12
10
  attributeParser?: (oldAttribute: string, transformFunction: TransformFunction) => string;
13
11
  }
14
- type TransformFunction = (oldAttribute: string) => string;
15
- interface AttributeParser {
12
+ export type TransformFunction = (oldAttribute: string) => string;
13
+ export interface AttributeParser {
16
14
  (oldAttribute: string, transformFunction: TransformFunction): string;
17
15
  }
18
- interface AttributeParsers {
16
+ export interface AttributeParsers {
19
17
  [index: string]: AttributeParser;
20
18
  }
21
19
  /**
@@ -24,7 +22,7 @@ interface AttributeParsers {
24
22
  * attributes that identify CDN-able resources converted to
25
23
  * use the given CDN URL.
26
24
  */
27
- declare class HtmlAttributeStreamTransformer extends HtmlTransformer {
25
+ export declare class HtmlAttributeStreamTransformer extends HtmlTransformer {
28
26
  options: HtmlAttributeStreamTransformerOptions;
29
27
  condensedTransformOptions: TransformDefinition[];
30
28
  constructor(options: HtmlAttributeStreamTransformerOptions);
@@ -32,5 +30,4 @@ declare class HtmlAttributeStreamTransformer extends HtmlTransformer {
32
30
  protected applyTransforms(): void;
33
31
  private processTransforms;
34
32
  }
35
-
36
- export { type AttributeParser, type AttributeParsers, HtmlAttributeStreamTransformer, type HtmlAttributeStreamTransformerOptions, type TransformDefinition, type TransformFunction };
33
+ //# sourceMappingURL=HtmlAttributeStreamTransformer.d.ts.map
@@ -1,7 +1,79 @@
1
- import {
2
- HtmlAttributeStreamTransformer
3
- } from "./chunk-UFAXQSS4.js";
4
- import "./chunk-DUQPDAQ6.js";
1
+ import { groupBy } from "lodash";
2
+ import HtmlTransformer from "./HtmlTransformer";
3
+ class HtmlAttributeStreamTransformer extends HtmlTransformer {
4
+ constructor(options) {
5
+ super();
6
+ this.options = options;
7
+ this.processTransforms();
8
+ this.applyTransforms();
9
+ }
10
+ condensedTransformOptions = [];
11
+ static attributeParsers = {
12
+ default: (attr, transformFunction) => transformFunction(attr)
13
+ };
14
+ applyTransforms() {
15
+ this.condensedTransformOptions.forEach((settings) => {
16
+ const selector = settings.selector;
17
+ const attribute = settings.attribute;
18
+ const parser = settings.attributeParser;
19
+ this.stream.selectAll(selector, (elem) => {
20
+ const oldAttributeValue = elem.getAttribute(attribute);
21
+ if (typeof oldAttributeValue === "string") {
22
+ const newAttributeValue = parser && parser(oldAttributeValue, this.options.transformFunction);
23
+ if (newAttributeValue && newAttributeValue !== oldAttributeValue) {
24
+ elem.setAttribute(attribute, newAttributeValue);
25
+ }
26
+ }
27
+ });
28
+ });
29
+ const { attributeToMarkElementToBeIgnored } = this.options;
30
+ if (attributeToMarkElementToBeIgnored) {
31
+ this.stream.selectAll(
32
+ `[${attributeToMarkElementToBeIgnored}]`,
33
+ (elem) => {
34
+ elem.removeAttribute(attributeToMarkElementToBeIgnored);
35
+ }
36
+ );
37
+ }
38
+ }
39
+ processTransforms() {
40
+ this.options.transformDefinitions.forEach((option) => {
41
+ if (!option.attributeParser) {
42
+ option.attributeParser = HtmlAttributeStreamTransformer.attributeParsers["default"];
43
+ }
44
+ if (typeof option.selector !== "string") {
45
+ throw new Error(`Invalid selector "${option.selector}" specified.`);
46
+ }
47
+ if (typeof option.attribute !== "string") {
48
+ throw new Error(`Invalid attribute "${option.attribute}" specified.`);
49
+ }
50
+ if (!(option.attributeParser instanceof Function)) {
51
+ throw new Error("Invalid attributeParser specified.");
52
+ }
53
+ });
54
+ const groupedTransformOptions = groupBy(
55
+ this.options.transformDefinitions,
56
+ (obj) => {
57
+ return [obj.attribute, obj.attributeParser];
58
+ }
59
+ );
60
+ this.condensedTransformOptions = Object.keys(groupedTransformOptions).map(
61
+ (key) => {
62
+ const likeTransformOptions = groupedTransformOptions[key];
63
+ if (likeTransformOptions == null || likeTransformOptions[0] == null) {
64
+ throw new Error(`No likeTransformOptions`);
65
+ }
66
+ const attribute = likeTransformOptions[0].attribute;
67
+ const attributeParser = likeTransformOptions[0].attributeParser;
68
+ return {
69
+ attribute,
70
+ attributeParser,
71
+ selector: likeTransformOptions.map((likeTransformOption) => likeTransformOption.selector).join(", ")
72
+ };
73
+ }
74
+ );
75
+ }
76
+ }
5
77
  export {
6
78
  HtmlAttributeStreamTransformer
7
79
  };
@@ -1,8 +1,9 @@
1
- import { Trumpet } from '@gofunky/trumpet';
2
-
3
- interface TrumpetTransformStream extends Trumpet {
1
+ /// <reference types="./types/trumpet.d.ts" />
2
+ /// <reference types="node" />
3
+ import { type Trumpet } from "@gofunky/trumpet";
4
+ export interface TrumpetTransformStream extends Trumpet {
4
5
  }
5
- declare class HtmlTransformer {
6
+ export default class HtmlTransformer {
6
7
  private transformStream;
7
8
  constructor();
8
9
  /**
@@ -14,5 +15,4 @@ declare class HtmlTransformer {
14
15
  */
15
16
  get outputBufferPromise(): Promise<Buffer>;
16
17
  }
17
-
18
- export { type TrumpetTransformStream, HtmlTransformer as default };
18
+ //# sourceMappingURL=HtmlTransformer.d.ts.map
@@ -1,6 +1,23 @@
1
- import {
2
- HtmlTransformer
3
- } from "./chunk-DUQPDAQ6.js";
1
+ import trumpet from "@gofunky/trumpet";
2
+ import streamToPromise from "stream-to-promise";
3
+ class HtmlTransformer {
4
+ transformStream;
5
+ constructor() {
6
+ this.transformStream = trumpet();
7
+ }
8
+ /**
9
+ * Get the underlying transform stream
10
+ */
11
+ get stream() {
12
+ return this.transformStream;
13
+ }
14
+ /**
15
+ * Get a promise to the complete output Buffer (alternative API to dealing with stream)
16
+ */
17
+ get outputBufferPromise() {
18
+ return streamToPromise(this.stream);
19
+ }
20
+ }
4
21
  export {
5
22
  HtmlTransformer as default
6
23
  };
package/dist/cdnify.d.ts CHANGED
@@ -1,15 +1,12 @@
1
- import { CDNTransformerOptions } from './CDNTransformer.js';
2
- export { CDNTransformFunction, CDNTransformer } from './CDNTransformer.js';
3
- import './HtmlAttributeStreamTransformer.js';
4
- import './HtmlTransformer.js';
5
- import '@gofunky/trumpet';
6
-
7
- interface CdnifyOptions extends CDNTransformerOptions {
1
+ /// <reference types="node" />
2
+ import { CDNTransformerOptions } from "./CDNTransformer";
3
+ export * from "./CDNTransformer";
4
+ export interface CdnifyOptions extends CDNTransformerOptions {
8
5
  buffer: string | Buffer;
9
6
  }
10
7
  /**
11
8
  * Alternative wrapper for simple Promise-based use of library (as opposed to transform stream)
12
9
  */
13
- declare function cdnify(options: CdnifyOptions): Promise<Buffer>;
14
-
15
- export { CDNTransformerOptions, type CdnifyOptions, cdnify, cdnify as default };
10
+ export declare function cdnify(options: CdnifyOptions): Promise<Buffer>;
11
+ export default cdnify;
12
+ //# sourceMappingURL=cdnify.d.ts.map
package/dist/cdnify.js CHANGED
@@ -1,11 +1,5 @@
1
- import {
2
- CDNTransformer
3
- } from "./chunk-YUWY5QAX.js";
4
- import "./chunk-UFAXQSS4.js";
5
- import "./chunk-DUQPDAQ6.js";
6
- import "./chunk-ZHBDQZLT.js";
7
-
8
- // src/cdnify.ts
1
+ import { CDNTransformer } from "./CDNTransformer";
2
+ export * from "./CDNTransformer";
9
3
  import streamifier from "streamifier";
10
4
  function cdnify(options) {
11
5
  if (typeof options.buffer !== "string") {
@@ -17,7 +11,6 @@ function cdnify(options) {
17
11
  }
18
12
  var cdnify_default = cdnify;
19
13
  export {
20
- CDNTransformer,
21
14
  cdnify,
22
15
  cdnify_default as default
23
16
  };
@@ -0,0 +1,3 @@
1
+ export * from "./cdnify";
2
+ export * from "./CDNTransformer";
3
+ //# sourceMappingURL=index.d.ts.map
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export * from "./cdnify";
2
+ export * from "./CDNTransformer";
@@ -2,5 +2,5 @@
2
2
  * Resolve oldUrl against pathOldUrlIsRelativeTo, and then prepend newUrlBase
3
3
  */
4
4
  declare const _default: (newUrlBase: string, oldUrl: string, pathOldUrlIsRelativeTo?: string) => string;
5
-
6
- export { _default as default };
5
+ export default _default;
6
+ //# sourceMappingURL=urlConverter.d.ts.map
@@ -1,6 +1,30 @@
1
- import {
2
- urlConverter_default
3
- } from "./chunk-ZHBDQZLT.js";
1
+ import url from "node:url";
2
+ import path from "node:path";
3
+ function isLocalPath(filePath) {
4
+ return typeof filePath === "string" && filePath.length > 0 && filePath.indexOf("//") === -1 && filePath.indexOf("data:") !== 0;
5
+ }
6
+ var urlConverter_default = (newUrlBase, oldUrl, pathOldUrlIsRelativeTo) => {
7
+ if (!isLocalPath(oldUrl)) {
8
+ return oldUrl;
9
+ } else {
10
+ oldUrl = oldUrl.trim();
11
+ let relativePath = ".";
12
+ if (pathOldUrlIsRelativeTo) {
13
+ const isDirAlready = pathOldUrlIsRelativeTo.endsWith("/");
14
+ const fileDirName = isDirAlready ? pathOldUrlIsRelativeTo : path.dirname(pathOldUrlIsRelativeTo) + "/";
15
+ relativePath = url.resolve(fileDirName, oldUrl);
16
+ } else {
17
+ relativePath = oldUrl;
18
+ }
19
+ if (relativePath.startsWith("/")) {
20
+ relativePath = relativePath.slice(1);
21
+ }
22
+ if (!newUrlBase.endsWith("/")) {
23
+ newUrlBase += "/";
24
+ }
25
+ return url.resolve(newUrlBase, relativePath);
26
+ }
27
+ };
4
28
  export {
5
29
  urlConverter_default as default
6
30
  };
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@altano/html-cdnify",
3
- "version": "2.0.0",
4
- "main": "dist/index.js",
5
- "types": "dist/index.d.ts",
3
+ "version": "3.0.0",
6
4
  "type": "module",
7
5
  "sideEffects": false,
6
+ "exports": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
8
  "description": "Transform the relative URLs in your HTML markup (e.g. scripts, stylesheets, images) to use your CDN URL.",
9
9
  "keywords": [
10
10
  "cdn",
@@ -15,8 +15,7 @@
15
15
  "files": [
16
16
  "assets/logo.png",
17
17
  "dist/**/*.js",
18
- "dist/**/*.d.ts",
19
- "README.md"
18
+ "dist/**/*.d.ts"
20
19
  ],
21
20
  "repository": {
22
21
  "type": "git",
@@ -45,11 +44,10 @@
45
44
  "@types/streamifier": "^0.1.2",
46
45
  "eslint": "^8.57.0",
47
46
  "prettier": "^3.2.5",
48
- "tsup": "^8.0.2",
49
47
  "typescript": "^5.4.2",
50
- "vitest": "^1.3.1",
48
+ "vitest": "^1.4.0",
51
49
  "@altano/tsconfig": "0.0.0",
52
- "eslint-config-altano": "0.0.1"
50
+ "eslint-config-altano": "0.0.0"
53
51
  },
54
52
  "engines": {
55
53
  "node": ">=5.10.1"
@@ -58,15 +56,18 @@
58
56
  "access": "public"
59
57
  },
60
58
  "scripts": {
61
- "build": "tsup",
62
- "build:types": "tsc --emitDeclarationOnly --declaration",
63
- "dev": "tsup --watch",
59
+ "build": "tsup --config build-config/tsup.config.node.ts",
60
+ "build:types": "tsc --project tsconfig.declarations.json",
61
+ "clean": "rm -rf .turbo && rm -rf .tsbuildinfo && rm -rf node_modules && rm -rf dist",
62
+ "dev": "pnpm run build --watch",
64
63
  "format": "prettier --check src",
65
64
  "format:fix": "prettier --write src",
66
- "lint": "eslint src/**/*.ts*",
67
- "lint:timing": "TIMING=1 eslint src/**/*.ts*",
68
- "lint:fix": "TIMING=1 eslint src/**/*.ts* --fix",
69
- "clean": "rm -rf .turbo && rm -rf .tsbuildinfo && rm -rf node_modules && rm -rf dist",
70
- "test": "vitest --run"
65
+ "lint": "eslint package.json src/**/*.ts*",
66
+ "lint:fix": "TIMING=1 pnpm lint --fix",
67
+ "lint:timing": "TIMING=1 pnpm lint",
68
+ "test:unit": "vitest --run",
69
+ "test:unit:watch": "vitest",
70
+ "typecheck:src": "tsc --noEmit",
71
+ "typecheck:test": "tsc --noEmit --project ./tests/tsconfig.json"
71
72
  }
72
73
  }
@@ -1,25 +0,0 @@
1
- // src/HtmlTransformer.ts
2
- import trumpet from "@gofunky/trumpet";
3
- import streamToPromise from "stream-to-promise";
4
- var HtmlTransformer = class {
5
- transformStream;
6
- constructor() {
7
- this.transformStream = trumpet();
8
- }
9
- /**
10
- * Get the underlying transform stream
11
- */
12
- get stream() {
13
- return this.transformStream;
14
- }
15
- /**
16
- * Get a promise to the complete output Buffer (alternative API to dealing with stream)
17
- */
18
- get outputBufferPromise() {
19
- return streamToPromise(this.stream);
20
- }
21
- };
22
-
23
- export {
24
- HtmlTransformer
25
- };
@@ -1,84 +0,0 @@
1
- import {
2
- HtmlTransformer
3
- } from "./chunk-DUQPDAQ6.js";
4
-
5
- // src/HtmlAttributeStreamTransformer.ts
6
- import { groupBy } from "lodash";
7
- var HtmlAttributeStreamTransformer = class _HtmlAttributeStreamTransformer extends HtmlTransformer {
8
- constructor(options) {
9
- super();
10
- this.options = options;
11
- this.processTransforms();
12
- this.applyTransforms();
13
- }
14
- condensedTransformOptions = [];
15
- static attributeParsers = {
16
- default: (attr, transformFunction) => transformFunction(attr)
17
- };
18
- applyTransforms() {
19
- this.condensedTransformOptions.forEach((settings) => {
20
- const selector = settings.selector;
21
- const attribute = settings.attribute;
22
- const parser = settings.attributeParser;
23
- this.stream.selectAll(selector, (elem) => {
24
- const oldAttributeValue = elem.getAttribute(attribute);
25
- if (typeof oldAttributeValue === "string") {
26
- const newAttributeValue = parser && parser(oldAttributeValue, this.options.transformFunction);
27
- if (newAttributeValue && newAttributeValue !== oldAttributeValue) {
28
- elem.setAttribute(attribute, newAttributeValue);
29
- }
30
- }
31
- });
32
- });
33
- const { attributeToMarkElementToBeIgnored } = this.options;
34
- if (attributeToMarkElementToBeIgnored) {
35
- this.stream.selectAll(
36
- `[${attributeToMarkElementToBeIgnored}]`,
37
- (elem) => {
38
- elem.removeAttribute(attributeToMarkElementToBeIgnored);
39
- }
40
- );
41
- }
42
- }
43
- processTransforms() {
44
- this.options.transformDefinitions.forEach((option) => {
45
- if (!option.attributeParser) {
46
- option.attributeParser = _HtmlAttributeStreamTransformer.attributeParsers["default"];
47
- }
48
- if (typeof option.selector !== "string") {
49
- throw new Error(`Invalid selector "${option.selector}" specified.`);
50
- }
51
- if (typeof option.attribute !== "string") {
52
- throw new Error(`Invalid attribute "${option.attribute}" specified.`);
53
- }
54
- if (!(option.attributeParser instanceof Function)) {
55
- throw new Error("Invalid attributeParser specified.");
56
- }
57
- });
58
- const groupedTransformOptions = groupBy(
59
- this.options.transformDefinitions,
60
- (obj) => {
61
- return [obj.attribute, obj.attributeParser];
62
- }
63
- );
64
- this.condensedTransformOptions = Object.keys(groupedTransformOptions).map(
65
- (key) => {
66
- const likeTransformOptions = groupedTransformOptions[key];
67
- if (likeTransformOptions == null || likeTransformOptions[0] == null) {
68
- throw new Error(`No likeTransformOptions`);
69
- }
70
- const attribute = likeTransformOptions[0].attribute;
71
- const attributeParser = likeTransformOptions[0].attributeParser;
72
- return {
73
- attribute,
74
- attributeParser,
75
- selector: likeTransformOptions.map((likeTransformOption) => likeTransformOption.selector).join(", ")
76
- };
77
- }
78
- );
79
- }
80
- };
81
-
82
- export {
83
- HtmlAttributeStreamTransformer
84
- };
@@ -1,132 +0,0 @@
1
- import {
2
- HtmlAttributeStreamTransformer
3
- } from "./chunk-UFAXQSS4.js";
4
- import {
5
- urlConverter_default
6
- } from "./chunk-ZHBDQZLT.js";
7
-
8
- // src/CDNTransformer.ts
9
- import { unionBy, merge, mergeWith } from "lodash";
10
- var OptionsMerger = class _OptionsMerger {
11
- static isTransformDefinition(a) {
12
- return a != null && typeof a === "object" && "selector" in a && "attribute" in a && typeof a.selector === "string" && typeof a.selector === "string" && typeof a.attribute === "string";
13
- }
14
- static isTransformDefinitions(a) {
15
- return Array.isArray(a) && a.length > 0 && _OptionsMerger.isTransformDefinition(a[0]);
16
- }
17
- static mergeCustomizer = (objValue, srcValue) => {
18
- if (_OptionsMerger.isTransformDefinitions(objValue) || _OptionsMerger.isTransformDefinitions(srcValue)) {
19
- const result = unionBy(
20
- srcValue,
21
- objValue,
22
- // reverse order to allow srcValue to take precedence
23
- (value) => `${value.selector} / ${value.attribute}`
24
- );
25
- return result;
26
- } else {
27
- return void 0;
28
- }
29
- };
30
- static merge(target, source1, source2, source3) {
31
- return mergeWith(
32
- target,
33
- source1,
34
- source2,
35
- source3,
36
- _OptionsMerger.mergeCustomizer
37
- );
38
- }
39
- };
40
- var CDNTransformer = class _CDNTransformer extends HtmlAttributeStreamTransformer {
41
- cdnOptions;
42
- constructor(cdnOptions) {
43
- super(
44
- OptionsMerger.merge(
45
- {},
46
- _CDNTransformer.#defaultHtmlAttributeStreamTransformerOptions,
47
- {
48
- transformDefinitions: [],
49
- transformFunction: (attribute) => this.#cdnifyAttribute(attribute)
50
- },
51
- {
52
- transformDefinitions: cdnOptions.transformDefinitions
53
- }
54
- )
55
- );
56
- this.cdnOptions = merge(
57
- {
58
- transformDefinitions: []
59
- },
60
- _CDNTransformer.#defaultCdnOptions,
61
- cdnOptions
62
- );
63
- if (typeof this.cdnOptions.cdnUrl !== "string") {
64
- throw new Error(`Invalid cdnUrl "${this.cdnOptions.cdnUrl}" specified.`);
65
- }
66
- }
67
- #cdnifyAttribute(oldUrl) {
68
- return this.cdnOptions.transformFunction(
69
- this.cdnOptions.cdnUrl,
70
- oldUrl,
71
- this.cdnOptions.bufferPath
72
- );
73
- }
74
- static #defaultCdnOptions = {
75
- bufferPath: ".",
76
- transformFunction: urlConverter_default
77
- };
78
- static defaultTransformFunction = _CDNTransformer.#defaultCdnOptions.transformFunction;
79
- static #defaultHtmlAttributeStreamTransformerOptions = {
80
- attributeToMarkElementToBeIgnored: "data-cdn-ignore",
81
- // inspired by https://github.com/callumlocke/grunt-cdnify/blob/master/tasks/cdnify.js#L31
82
- transformDefinitions: [
83
- {
84
- selector: `video[poster]:not([data-cdn-ignore])`,
85
- attribute: "poster"
86
- },
87
- {
88
- selector: `img[data-src]:not([data-cdn-ignore])`,
89
- attribute: "data-src"
90
- },
91
- {
92
- selector: `script[src]:not([data-cdn-ignore])`,
93
- attribute: "src"
94
- },
95
- {
96
- selector: `source[src]:not([data-cdn-ignore])`,
97
- attribute: "src"
98
- },
99
- {
100
- selector: `link[rel="apple-touch-icon"]:not([data-cdn-ignore])`,
101
- attribute: "href"
102
- },
103
- {
104
- selector: `link[rel="icon"]:not([data-cdn-ignore])`,
105
- attribute: "href"
106
- },
107
- {
108
- selector: `link[rel="shortcut icon"]:not([data-cdn-ignore])`,
109
- attribute: "href"
110
- },
111
- {
112
- selector: `link[rel="stylesheet"]:not([data-cdn-ignore])`,
113
- attribute: "href"
114
- },
115
- {
116
- selector: `img[src]:not([data-cdn-ignore])`,
117
- attribute: "src"
118
- },
119
- {
120
- selector: `img[srcset]:not([data-cdn-ignore])`,
121
- attribute: "srcset",
122
- attributeParser(attr, transformFunction) {
123
- return attr.split(",").map((imgInfo) => imgInfo.replace(/([^ ]+)/, transformFunction)).join(",");
124
- }
125
- }
126
- ]
127
- };
128
- };
129
-
130
- export {
131
- CDNTransformer
132
- };
@@ -1,32 +0,0 @@
1
- // src/urlConverter.ts
2
- import url from "node:url";
3
- import path from "node:path";
4
- function isLocalPath(filePath) {
5
- return typeof filePath === "string" && filePath.length > 0 && filePath.indexOf("//") === -1 && filePath.indexOf("data:") !== 0;
6
- }
7
- var urlConverter_default = (newUrlBase, oldUrl, pathOldUrlIsRelativeTo) => {
8
- if (!isLocalPath(oldUrl)) {
9
- return oldUrl;
10
- } else {
11
- oldUrl = oldUrl.trim();
12
- let relativePath = ".";
13
- if (pathOldUrlIsRelativeTo) {
14
- const isDirAlready = pathOldUrlIsRelativeTo.endsWith("/");
15
- const fileDirName = isDirAlready ? pathOldUrlIsRelativeTo : path.dirname(pathOldUrlIsRelativeTo) + "/";
16
- relativePath = url.resolve(fileDirName, oldUrl);
17
- } else {
18
- relativePath = oldUrl;
19
- }
20
- if (relativePath.startsWith("/")) {
21
- relativePath = relativePath.slice(1);
22
- }
23
- if (!newUrlBase.endsWith("/")) {
24
- newUrlBase += "/";
25
- }
26
- return url.resolve(newUrlBase, relativePath);
27
- }
28
- };
29
-
30
- export {
31
- urlConverter_default
32
- };