remark-inline-svg-flex 0.4.1 → 0.5.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/.github/workflows/release.yml +2 -1
- package/README.md +40 -15
- package/dist/cjs/plugin.js +30 -7
- package/dist/plugin.js +30 -7
- package/package.json +3 -3
|
@@ -7,6 +7,7 @@ on:
|
|
|
7
7
|
paths:
|
|
8
8
|
- package.json
|
|
9
9
|
- src/**
|
|
10
|
+
- .github/workflows/**
|
|
10
11
|
|
|
11
12
|
jobs:
|
|
12
13
|
release:
|
|
@@ -43,7 +44,7 @@ jobs:
|
|
|
43
44
|
env:
|
|
44
45
|
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
45
46
|
run: |
|
|
46
|
-
npm
|
|
47
|
+
npm i
|
|
47
48
|
npm run tests
|
|
48
49
|
npm run build
|
|
49
50
|
git config user.name "github-actions"
|
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
> ⚠️ Beta Nearing v1.0.0 — APIs may
|
|
1
|
+
> ⚠️ Beta Nearing v1.0.0 — Still under development! APIs may change.
|
|
2
2
|
|
|
3
3
|
# remark-inline-svg-flex
|
|
4
4
|
|
|
@@ -17,13 +17,14 @@ Flexible Remark plugin that inlines and optimizes SVGs with SVGO, featuring cust
|
|
|
17
17
|
- [`assetsDir`](#assetsdir)
|
|
18
18
|
- [`wrapper`](#wrapper)
|
|
19
19
|
- [`svgo`](#svgo)
|
|
20
|
+
- [Paths supported](#path-supported)
|
|
20
21
|
- [SVGO configuration](#svgo-configuration)
|
|
21
22
|
|
|
22
23
|
## Features
|
|
23
24
|
|
|
24
25
|
### ✔️ Robust and customizable path resolution
|
|
25
26
|
|
|
26
|
-
- If the SVG path is absolute, it
|
|
27
|
+
- If the SVG path is absolute, it will use the project directory as root.
|
|
27
28
|
- If the path is relative and `assetsDir` is defined, it is resolved relative to the `assetsDir` directory.
|
|
28
29
|
- Otherwise, the path is resolved relative to the Markdown file’s location.
|
|
29
30
|
|
|
@@ -54,39 +55,49 @@ npm i remark-inline-svg-flex
|
|
|
54
55
|
Say we have the following file `example.md`:
|
|
55
56
|
|
|
56
57
|
```markdown
|
|
57
|
-
#
|
|
58
|
+
# Some Title
|
|
58
59
|
|
|
59
60
|
This is a test markdown document.
|
|
60
61
|
|
|
61
|
-

|
|
62
63
|
```
|
|
63
64
|
|
|
64
|
-
And our module `example.js` looks as the one below
|
|
65
|
+
And our module `example.js` looks as the one below:
|
|
65
66
|
|
|
66
67
|
```js
|
|
67
68
|
import { remark } from 'remark';
|
|
68
|
-
import
|
|
69
|
+
import remarkParse from 'remark-parse';
|
|
69
70
|
import { remarkInlineSvg } from 'remark-inline-svg-flex';
|
|
71
|
+
import { readFile } from 'node:fs/promises';
|
|
72
|
+
|
|
73
|
+
const filePath = './tests/fixtures/example.md';
|
|
74
|
+
const markdownString = await readFile(filePath, { encoding: 'utf8' });
|
|
70
75
|
|
|
71
|
-
|
|
76
|
+
/**
|
|
77
|
+
* `filePath` is used as the virtual file path so that relative links
|
|
78
|
+
* inside the markdown file can be resolved correctly by the plugin.
|
|
79
|
+
*/
|
|
80
|
+
async function myProcess(markdown, filePath) {
|
|
81
|
+
return await remark()
|
|
82
|
+
.use(remarkParse)
|
|
83
|
+
.use(remarkInlineSvg)
|
|
84
|
+
.process({ value: markdown, path: filePath });
|
|
85
|
+
}
|
|
72
86
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
.process({ value: markdown, path: path });
|
|
87
|
+
const result = await myProcess(markdownString, filePath);
|
|
88
|
+
|
|
89
|
+
console.log(String(result.value));
|
|
77
90
|
```
|
|
78
91
|
|
|
79
92
|
Now running `node example.js` yields:
|
|
80
93
|
|
|
81
94
|
```markdown
|
|
82
|
-
#
|
|
95
|
+
# Some Title
|
|
83
96
|
|
|
84
97
|
This is a test markdown document.
|
|
85
98
|
|
|
86
99
|
<figure class="inline-svg">
|
|
87
|
-
<svg width="800" height="800" fill="none" viewBox="0 0 16 16">
|
|
88
|
-
...
|
|
89
|
-
</svg>
|
|
100
|
+
<svg width="800" height="800" fill="none" viewBox="0 0 16 16"><path fill="#000" fill-rule="evenodd" d="m8 16-4.458-3.662A6.96 6.96 0 0 1 1 6.96C1 3.116 4.156 0 8 0s7 3.116 7 6.96a6.96 6.96 0 0 1-2.542 5.378zM3 6h2a2 2 0 0 1 2 2v1L3 7.5zm8 0a2 2 0 0 0-2 2v1l4-1.5V6z" clip-rule="evenodd"/></svg>
|
|
90
101
|
</figure>
|
|
91
102
|
```
|
|
92
103
|
|
|
@@ -122,6 +133,20 @@ Defines the HTML wrapper used around the inlined SVG.
|
|
|
122
133
|
|
|
123
134
|
The SVG's are optimized by default. Disable it by setting it to `false`.
|
|
124
135
|
|
|
136
|
+
## Path supported
|
|
137
|
+
|
|
138
|
+
Remote URLs `http://`, `https://` and other external URLs are ignored.
|
|
139
|
+
|
|
140
|
+
Examples of supported paths:
|
|
141
|
+
|
|
142
|
+
```
|
|
143
|
+
example.svg
|
|
144
|
+
/example.svg
|
|
145
|
+
/tests/assets/example.svg
|
|
146
|
+
./example.svg
|
|
147
|
+
../assets/example.svg
|
|
148
|
+
```
|
|
149
|
+
|
|
125
150
|
## SVGO configuration
|
|
126
151
|
|
|
127
152
|
SVGO is configured to use the [default preset](https://svgo.dev/docs/preset-default/) of plugins and the [removeXMLNS](https://svgo.dev/docs/plugins/removeXMLNS/) plugin.
|
package/dist/cjs/plugin.js
CHANGED
|
@@ -21,12 +21,12 @@ const remarkInlineSvg = (consumerOptions = {}) => {
|
|
|
21
21
|
wrapper: consumerOptions.wrapper ?? '<figure class="inline-svg"></figure>',
|
|
22
22
|
svgo: consumerOptions.svgo ?? true,
|
|
23
23
|
};
|
|
24
|
-
return function transformer(tree,
|
|
24
|
+
return function transformer(tree, vFile) {
|
|
25
25
|
(0, unist_util_visit_1.visit)(tree, 'image', (node, i, parent) => {
|
|
26
|
-
if (!node.url?.endsWith(options.suffix) || !parent)
|
|
26
|
+
if (!node.url?.endsWith(options.suffix) || !parent || isHttpUrl(node.url))
|
|
27
27
|
return;
|
|
28
28
|
try {
|
|
29
|
-
const svgPath = resolvePath(options.assetsDir, node,
|
|
29
|
+
const svgPath = resolvePath(options.assetsDir, node, vFile.history[0] && node_path_1.default.dirname(vFile.history[0]), vFile.cwd);
|
|
30
30
|
const svgString = processSvg(svgPath, options.svgo);
|
|
31
31
|
if (typeof i === 'number') {
|
|
32
32
|
parent.children[i] = {
|
|
@@ -65,15 +65,19 @@ function wrap(svgString, htmlWrapper) {
|
|
|
65
65
|
* 2) `assetsDir` → relative to it
|
|
66
66
|
* 3) fallback → relative to Markdown file directory (if markdown file directory is undefined use absolute URL)
|
|
67
67
|
*/
|
|
68
|
-
function resolvePath(assetsDir, node,
|
|
68
|
+
function resolvePath(assetsDir, node, vFileDir, vFileCwd) {
|
|
69
|
+
const root = vFileCwd ?? process.cwd();
|
|
69
70
|
if (node_path_1.default.isAbsolute(node.url)) {
|
|
70
|
-
|
|
71
|
+
// absolute URL attempting to use the project directory as root (uses `process.cwd()` as fallback)
|
|
72
|
+
return node_path_1.default.resolve(root, normalizePath(node.url));
|
|
71
73
|
}
|
|
72
74
|
else if (assetsDir) {
|
|
73
|
-
|
|
75
|
+
// relative to assetsDir specified by consumer
|
|
76
|
+
return node_path_1.default.resolve(root, normalizePath(assetsDir), node.url);
|
|
74
77
|
}
|
|
75
78
|
else {
|
|
76
|
-
|
|
79
|
+
// relative to Markdown file directory
|
|
80
|
+
return node_path_1.default.resolve(vFileDir ?? root, node.url);
|
|
77
81
|
}
|
|
78
82
|
}
|
|
79
83
|
/**
|
|
@@ -84,3 +88,22 @@ function optimizeSvg(svgString) {
|
|
|
84
88
|
plugins: ['preset-default', 'removeXMLNS'],
|
|
85
89
|
});
|
|
86
90
|
}
|
|
91
|
+
function normalizePath(path) {
|
|
92
|
+
let normalizedPath = path;
|
|
93
|
+
if (path.startsWith('/')) {
|
|
94
|
+
normalizedPath = normalizedPath.slice(1);
|
|
95
|
+
}
|
|
96
|
+
if (!path.endsWith('/')) {
|
|
97
|
+
normalizedPath = normalizedPath + '/';
|
|
98
|
+
}
|
|
99
|
+
return normalizedPath;
|
|
100
|
+
}
|
|
101
|
+
function isHttpUrl(url) {
|
|
102
|
+
try {
|
|
103
|
+
const parsed = new URL(url);
|
|
104
|
+
return parsed.protocol === 'http:' || parsed.protocol === 'https:';
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
}
|
package/dist/plugin.js
CHANGED
|
@@ -19,12 +19,12 @@ const remarkInlineSvg = (consumerOptions = {}) => {
|
|
|
19
19
|
wrapper: consumerOptions.wrapper ?? '<figure class="inline-svg"></figure>',
|
|
20
20
|
svgo: consumerOptions.svgo ?? true,
|
|
21
21
|
};
|
|
22
|
-
return function transformer(tree,
|
|
22
|
+
return function transformer(tree, vFile) {
|
|
23
23
|
visit(tree, 'image', (node, i, parent) => {
|
|
24
|
-
if (!node.url?.endsWith(options.suffix) || !parent)
|
|
24
|
+
if (!node.url?.endsWith(options.suffix) || !parent || isHttpUrl(node.url))
|
|
25
25
|
return;
|
|
26
26
|
try {
|
|
27
|
-
const svgPath = resolvePath(options.assetsDir, node,
|
|
27
|
+
const svgPath = resolvePath(options.assetsDir, node, vFile.history[0] && path.dirname(vFile.history[0]), vFile.cwd);
|
|
28
28
|
const svgString = processSvg(svgPath, options.svgo);
|
|
29
29
|
if (typeof i === 'number') {
|
|
30
30
|
parent.children[i] = {
|
|
@@ -62,15 +62,19 @@ function wrap(svgString, htmlWrapper) {
|
|
|
62
62
|
* 2) `assetsDir` → relative to it
|
|
63
63
|
* 3) fallback → relative to Markdown file directory (if markdown file directory is undefined use absolute URL)
|
|
64
64
|
*/
|
|
65
|
-
function resolvePath(assetsDir, node,
|
|
65
|
+
function resolvePath(assetsDir, node, vFileDir, vFileCwd) {
|
|
66
|
+
const root = vFileCwd ?? process.cwd();
|
|
66
67
|
if (path.isAbsolute(node.url)) {
|
|
67
|
-
|
|
68
|
+
// absolute URL attempting to use the project directory as root (uses `process.cwd()` as fallback)
|
|
69
|
+
return path.resolve(root, normalizePath(node.url));
|
|
68
70
|
}
|
|
69
71
|
else if (assetsDir) {
|
|
70
|
-
|
|
72
|
+
// relative to assetsDir specified by consumer
|
|
73
|
+
return path.resolve(root, normalizePath(assetsDir), node.url);
|
|
71
74
|
}
|
|
72
75
|
else {
|
|
73
|
-
|
|
76
|
+
// relative to Markdown file directory
|
|
77
|
+
return path.resolve(vFileDir ?? root, node.url);
|
|
74
78
|
}
|
|
75
79
|
}
|
|
76
80
|
/**
|
|
@@ -81,4 +85,23 @@ function optimizeSvg(svgString) {
|
|
|
81
85
|
plugins: ['preset-default', 'removeXMLNS'],
|
|
82
86
|
});
|
|
83
87
|
}
|
|
88
|
+
function normalizePath(path) {
|
|
89
|
+
let normalizedPath = path;
|
|
90
|
+
if (path.startsWith('/')) {
|
|
91
|
+
normalizedPath = normalizedPath.slice(1);
|
|
92
|
+
}
|
|
93
|
+
if (!path.endsWith('/')) {
|
|
94
|
+
normalizedPath = normalizedPath + '/';
|
|
95
|
+
}
|
|
96
|
+
return normalizedPath;
|
|
97
|
+
}
|
|
98
|
+
function isHttpUrl(url) {
|
|
99
|
+
try {
|
|
100
|
+
const parsed = new URL(url);
|
|
101
|
+
return parsed.protocol === 'http:' || parsed.protocol === 'https:';
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
84
107
|
export { remarkInlineSvg };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "remark-inline-svg-flex",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Customizable Remark plugin to inline and optimize SVGs with SVGO, with robust path resolution.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"remark",
|
|
@@ -23,12 +23,12 @@
|
|
|
23
23
|
"type": "module",
|
|
24
24
|
"main": "./dist/cjs/index.js",
|
|
25
25
|
"module": "./dist/index.js",
|
|
26
|
-
"types": "./dist/
|
|
26
|
+
"types": "./dist/index.d.ts",
|
|
27
27
|
"exports": {
|
|
28
28
|
".": {
|
|
29
29
|
"import": "./dist/index.js",
|
|
30
30
|
"require": "./dist/cjs/index.js",
|
|
31
|
-
"types": "./dist/
|
|
31
|
+
"types": "./dist/index.d.ts"
|
|
32
32
|
}
|
|
33
33
|
},
|
|
34
34
|
"scripts": {
|