@compiiile/compiiile 2.16.1 → 2.17.1
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/.compiiile/src/layouts/BaseLayout.astro +26 -0
- package/.compiiile/src/style/texts.css +0 -1
- package/4-pro-features.mdx +6 -6
- package/README.md +28 -22
- package/bin/config.js +4 -141
- package/bin/loadConfig.js +175 -0
- package/bin/vitePluginCompiiile/index.js +102 -1
- package/bin/vitePluginCompiiile/models/Context.js +23 -17
- package/package.json +1 -1
- package/.compiiile/components.d.ts +0 -23
- package/bin/.compiiile/src/env.d.ts +0 -1
|
@@ -40,5 +40,31 @@ const defaultTheme = site.theme
|
|
|
40
40
|
</head>
|
|
41
41
|
<body>
|
|
42
42
|
<slot></slot>
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
<script>
|
|
46
|
+
/*
|
|
47
|
+
Using HMR, we send a custom event when the `asSlides` frontmatter parameter changed.
|
|
48
|
+
The event contains the old and new route path based on whether the page needs to display slides.
|
|
49
|
+
We store the new page URL to redirect to in the sessionStorage because it follows this flow:
|
|
50
|
+
- the `switch-page-render` event is sent
|
|
51
|
+
- we receive it just below, and check if we are on the updated file's URL : we store the new URL to redirect to
|
|
52
|
+
- the whole page reloads because Compiiile vite's module in invalidated to reload its config and all files
|
|
53
|
+
- only when the reload is done, we get to the second `if`, where we change the current route URL
|
|
54
|
+
- the redirect is not made before the vite's module invalidation because the reload prevents the redirection from happening
|
|
55
|
+
*/
|
|
56
|
+
if (import.meta.hot) {
|
|
57
|
+
import.meta.hot.on('switch-page-render', (data) => {
|
|
58
|
+
if(window.location.pathname === data.oldRoutePath){
|
|
59
|
+
sessionStorage.setItem("COMPIIILE_SWITCH_PAGE_RENDER", window.location.origin + data.newRoutePath)
|
|
60
|
+
}
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if(sessionStorage.getItem("COMPIIILE_SWITCH_PAGE_RENDER")){
|
|
65
|
+
window.location.href = sessionStorage.getItem("COMPIIILE_SWITCH_PAGE_RENDER")
|
|
66
|
+
sessionStorage.removeItem("COMPIIILE_SWITCH_PAGE_RENDER")
|
|
67
|
+
}
|
|
68
|
+
</script>
|
|
43
69
|
</body>
|
|
44
70
|
</html>
|
package/4-pro-features.mdx
CHANGED
|
@@ -19,9 +19,9 @@ Here is an example of a CSS file content to set some colors and the default font
|
|
|
19
19
|
@import url(https://fonts.bunny.net/css?family=inter:200i,400,400i,900);
|
|
20
20
|
|
|
21
21
|
:root {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
--layout-background-color: #0000ff;
|
|
23
|
+
--darker-background-color: #00ff00;
|
|
24
|
+
--default-font: "Inter", sans-serif;
|
|
25
25
|
}
|
|
26
26
|
```
|
|
27
27
|
|
|
@@ -627,10 +627,10 @@ The `CDetails` component allows you to hide a block and reveal it by clicking on
|
|
|
627
627
|
</CDetails>
|
|
628
628
|
|
|
629
629
|
<CDetails summary="Source">
|
|
630
|
-
|
|
631
|
-
|
|
630
|
+
```mdx
|
|
631
|
+
<CDetails summary="Example">
|
|
632
632
|
> [!TIP]
|
|
633
633
|
> This is lit! :sparkles:
|
|
634
634
|
</CDetails>
|
|
635
|
-
|
|
635
|
+
```
|
|
636
636
|
</CDetails>
|
package/README.md
CHANGED
|
@@ -99,11 +99,16 @@ To make yourself an idea and quickly get started using Compiiile, here are some
|
|
|
99
99
|
terminal to get Compiiile running with a couple of markdown files as tests:
|
|
100
100
|
|
|
101
101
|
```bash
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
102
|
+
# creating a new folder and go into this folder
|
|
103
|
+
mkdir test-compiiile && cd test-compiiile
|
|
104
|
+
# installing compiiile as global dependency using npm
|
|
105
|
+
npm i -g @compiiile/compiiile
|
|
106
|
+
# a first test file
|
|
107
|
+
echo '# Test Compiiile\n\n> Here is a blockquote for you\n\n## Your markdown awaits below' > README.md
|
|
108
|
+
# a second test file as slides
|
|
109
|
+
echo '---\nasSlides: true\n---\n\n# Slide 1\n\n---\n\n# And this is slide 2' > slides.md
|
|
110
|
+
# running Compiiile for these 2 files
|
|
111
|
+
compiiile --title="📚 Compiiile"
|
|
107
112
|
```
|
|
108
113
|
|
|
109
114
|
Et voilà , you should be able to preview your files in your browser :tada:.
|
|
@@ -196,23 +201,24 @@ The home page of Compiiile (`/`) points to a `README.md` file located at the roo
|
|
|
196
201
|
|
|
197
202
|
Here is the list of parameters that you can set to customize Compiiile (none are required):
|
|
198
203
|
|
|
199
|
-
| Parameter | Type | Description
|
|
200
|
-
|
|
201
|
-
| `title` | `string` | The title to display on the top-left of the User Interface
|
|
202
|
-
| `description` | `string` | The description that is rendered by default for the SEO
|
|
203
|
-
| `logo` | `string` | The relative path of the logo to display in the TopBar and as favicon
|
|
204
|
-
| `logoUrl` | `string` | The url to go to when clicking on the logo, defaults to the home page if not set
|
|
205
|
-
| `dest` | `string` | The folder in which to build files, defaults to `./.compiiile/dist`
|
|
206
|
-
| `siteUrl` | `string` | The url of the website in production (without trailing slash), used for the SEO tag `og:image`
|
|
207
|
-
| `astroConfig` | `Object` | Override [default Astro config](https://docs.astro.build/en/reference/configuration-reference/)
|
|
208
|
-
| `data` | `Object` | An object with data to use in MDX files (check use case below)
|
|
209
|
-
| `theme` | `string` | The website theme, value can be : `auto` (default value: adapts to system preferences) \| `light` \| `dark`
|
|
210
|
-
| `useAutoTitles` | `Boolean` | If set to `true`, use the first file heading as title to be displayed in the navbar and for SEO. Defaults to `false`
|
|
211
|
-
| `noIndex` | `Boolean` | If set to `true`, the `robots.txt` file will disallow all routes, preventing indexation. Defaults to `false`
|
|
212
|
-
| `publicDir` | `string` | The folder name in which you can serve public files, defaults to `public`
|
|
213
|
-
| `vite.server.fs.allow` | `string[]` | Add local paths to vite's server fs allow list
|
|
214
|
-
| `printReady` | `Boolean` | Add a `/print` page to display a full ready-to-print content (uses `@compiiile/compiiile-print`)
|
|
215
|
-
| `css` | `string` | A relative path to a custom CSS file to customize the style <br/>:warning: Requires `compiiile-pro`
|
|
204
|
+
| Parameter | Type | Description |
|
|
205
|
+
|------------------------| ---------- |-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
206
|
+
| `title` | `string` | The title to display on the top-left of the User Interface |
|
|
207
|
+
| `description` | `string` | The description that is rendered by default for the SEO |
|
|
208
|
+
| `logo` | `string` | The relative path of the logo to display in the TopBar and as favicon |
|
|
209
|
+
| `logoUrl` | `string` | The url to go to when clicking on the logo, defaults to the home page if not set |
|
|
210
|
+
| `dest` | `string` | The folder in which to build files, defaults to `./.compiiile/dist` |
|
|
211
|
+
| `siteUrl` | `string` | The url of the website in production (without trailing slash), used for the SEO tag `og:image` |
|
|
212
|
+
| `astroConfig` | `Object` | Override [default Astro config](https://docs.astro.build/en/reference/configuration-reference/) |
|
|
213
|
+
| `data` | `Object` | An object with data to use in MDX files (check use case below) |
|
|
214
|
+
| `theme` | `string` | The website theme, value can be : `auto` (default value: adapts to system preferences) \| `light` \| `dark` |
|
|
215
|
+
| `useAutoTitles` | `Boolean` | If set to `true`, use the first file heading as title to be displayed in the navbar and for SEO. Defaults to `false` |
|
|
216
|
+
| `noIndex` | `Boolean` | If set to `true`, the `robots.txt` file will disallow all routes, preventing indexation. Defaults to `false` |
|
|
217
|
+
| `publicDir` | `string` | The folder name in which you can serve public files, defaults to `public` |
|
|
218
|
+
| `vite.server.fs.allow` | `string[]` | Add local paths to vite's server fs allow list |
|
|
219
|
+
| `printReady` | `Boolean` | Add a `/print` page to display a full ready-to-print content (uses `@compiiile/compiiile-print`) |
|
|
220
|
+
| `css` | `string` | A relative path to a custom CSS file to customize the style <br/>:warning: Requires `compiiile-pro` |
|
|
221
|
+
| `integrations` | `AstroIntegration[]` | An array of Astro Integrations for [custom Astro hooks implementations](https://docs.astro.build/en/reference/integrations-reference/#astroconfigsetup): injecting CSS, javascript, etc |
|
|
216
222
|
|
|
217
223
|
You can use these parameters in 2 ways:
|
|
218
224
|
|
package/bin/config.js
CHANGED
|
@@ -4,114 +4,17 @@ import { passthroughImageService } from "astro/config"
|
|
|
4
4
|
import compiiile from "./vitePluginCompiiile/index.js"
|
|
5
5
|
import mdx from "@astrojs/mdx"
|
|
6
6
|
import path from "node:path"
|
|
7
|
-
import { copyFileSync, cpSync, existsSync } from "node:fs"
|
|
8
7
|
import { fileURLToPath } from "node:url"
|
|
9
8
|
import markdownConfig from "./vitePluginCompiiile/markdownConfig.js"
|
|
10
9
|
import sitemap from "@astrojs/sitemap"
|
|
11
|
-
import { loadConfig } from "c12"
|
|
12
|
-
import yargs from "yargs/yargs"
|
|
13
|
-
import { hideBin } from "yargs/helpers"
|
|
14
10
|
|
|
15
|
-
import { readFile } from "node:fs/promises"
|
|
16
11
|
// Making sure fonts are accessible by vite's server
|
|
17
12
|
import { createRequire } from "node:module"
|
|
18
13
|
import { packageDirectory } from "pkg-dir"
|
|
19
14
|
|
|
20
|
-
|
|
21
|
-
process.env.COMPIIILE_SOURCE = source
|
|
15
|
+
import { loadConfig } from "./loadConfig.js"
|
|
22
16
|
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
/*
|
|
26
|
-
Order of options by priority:
|
|
27
|
-
1. command arguments
|
|
28
|
-
2. user-defined config in dedicated file
|
|
29
|
-
3. default config as fallback
|
|
30
|
-
*/
|
|
31
|
-
let configFromFile = {}
|
|
32
|
-
try {
|
|
33
|
-
configFromFile = (await loadConfig({ name: "compiiile" })).config
|
|
34
|
-
} catch {
|
|
35
|
-
// This means that no config file was provided: getting parameters from script parameters instead
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const argv = yargs(hideBin(process.argv))
|
|
39
|
-
.parserConfiguration({
|
|
40
|
-
"deep-merge-config": true
|
|
41
|
-
})
|
|
42
|
-
.alias("v", "version")
|
|
43
|
-
.config(configFromFile)
|
|
44
|
-
.command("dev", "launch development server")
|
|
45
|
-
.command("build", "build")
|
|
46
|
-
.command("preview", "preview")
|
|
47
|
-
.option("port", {
|
|
48
|
-
describe: "Port to use",
|
|
49
|
-
default: 4321
|
|
50
|
-
})
|
|
51
|
-
.option("host", {
|
|
52
|
-
describe: "Host to use",
|
|
53
|
-
default: "127.0.0.1"
|
|
54
|
-
})
|
|
55
|
-
.option("title", {
|
|
56
|
-
describe: "The title to display on the top-left of the User Interface"
|
|
57
|
-
})
|
|
58
|
-
.option("description", {
|
|
59
|
-
describe: "The description that is rendered by default for the SEO"
|
|
60
|
-
})
|
|
61
|
-
.option("logo", {
|
|
62
|
-
describe: "The relative path of the logo to display in the TopBar and as favicon"
|
|
63
|
-
})
|
|
64
|
-
.option("logoUrl", {
|
|
65
|
-
describe: "The url to go to when clicking on the logo, defaults to the home page if not set"
|
|
66
|
-
})
|
|
67
|
-
.option("dest", {
|
|
68
|
-
describe: "The folder in which to build files, defaults to `./.compiiile/dist`"
|
|
69
|
-
})
|
|
70
|
-
.option("siteUrl", {
|
|
71
|
-
describe: "The url of the website in production (without trailing slash), used for the SEO tag `og:image`"
|
|
72
|
-
})
|
|
73
|
-
.option("astroConfig", {
|
|
74
|
-
describe: "Override default Astro config (https://docs.astro.build/en/reference/configuration-reference/)"
|
|
75
|
-
})
|
|
76
|
-
.option("data", {
|
|
77
|
-
describe: "An object with data to use in MDX files"
|
|
78
|
-
})
|
|
79
|
-
.option("theme", {
|
|
80
|
-
describe:
|
|
81
|
-
"The website theme, value can be : `auto` (default value: adapts to system preferences) | `light` | `dark`"
|
|
82
|
-
})
|
|
83
|
-
.option("useAutoTitles", {
|
|
84
|
-
describe:
|
|
85
|
-
"If set to `true`, use the first file heading as title to be displayed in the navbar and for SEO. Defaults to `false`"
|
|
86
|
-
})
|
|
87
|
-
.option("noIndex", {
|
|
88
|
-
describe:
|
|
89
|
-
"If set to `true`, the `robots.txt` file will disallow all routes, preventing indexation. Defaults to `false`"
|
|
90
|
-
})
|
|
91
|
-
.option("publicDir", {
|
|
92
|
-
describe: "The folder name in which you can serve public files, defaults to `public`"
|
|
93
|
-
})
|
|
94
|
-
.option("vite.server.fs.allow", {
|
|
95
|
-
describe: "Add local paths to vite's server fs allow list"
|
|
96
|
-
})
|
|
97
|
-
.option("printReady", {
|
|
98
|
-
describe: "Add a /print page to display a full ready-to-print content (uses @compiiile/compiiile-print)"
|
|
99
|
-
})
|
|
100
|
-
.help()
|
|
101
|
-
.version(packageJSON.version).argv
|
|
102
|
-
|
|
103
|
-
process.env.VITE_COMPIIILE_SITE_URL = argv.siteUrl ?? ""
|
|
104
|
-
process.env.VITE_COMPIIILE_NO_INDEX = /true/i.test(argv.noIndex) // defaults to `false` if not set or not equal to `true`
|
|
105
|
-
|
|
106
|
-
process.env.VITE_COMPIIILE_TITLE = argv.title ?? ""
|
|
107
|
-
process.env.VITE_COMPIIILE_DESCRIPTION = argv.description ?? ""
|
|
108
|
-
|
|
109
|
-
process.env.VITE_COMPIIILE_LOGO_URL = argv.logoUrl ?? ""
|
|
110
|
-
|
|
111
|
-
process.env.VITE_COMPIIILE_THEME = argv.theme ?? "auto"
|
|
112
|
-
|
|
113
|
-
process.env.VITE_COMPIIILE_DATA = JSON.stringify(argv.data ?? {})
|
|
114
|
-
process.env.VITE_COMPIIILE_USE_AUTO_TITLES = /true/i.test(argv.useAutoTitles) // defaults to `false` if not set or not equal to `true`
|
|
17
|
+
const { argv, localIntegrations, configFromFile, source, hasPublicFiles, publicDir } = await loadConfig()
|
|
115
18
|
|
|
116
19
|
// Get command and set env
|
|
117
20
|
const IS_DEV = argv._.length === 0 || argv._.includes("dev")
|
|
@@ -126,35 +29,6 @@ if (IS_DEV) {
|
|
|
126
29
|
process.env.NODE_ENV = NODE_ENV_PRODUCTION
|
|
127
30
|
}
|
|
128
31
|
|
|
129
|
-
// Handling logo and favicon
|
|
130
|
-
process.env.VITE_COMPIIILE_LOGO = null
|
|
131
|
-
|
|
132
|
-
const publicDir = path.resolve(source, "./.compiiile/public")
|
|
133
|
-
|
|
134
|
-
const localPublicDirName = argv.publicDir ?? "public"
|
|
135
|
-
const localPublicDir = path.resolve(source, localPublicDirName)
|
|
136
|
-
const localPublicDirExists = existsSync(localPublicDir)
|
|
137
|
-
|
|
138
|
-
const hasPublicFiles = localPublicDirExists || argv.logo
|
|
139
|
-
if (hasPublicFiles) {
|
|
140
|
-
cpSync(fileURLToPath(new URL("../.compiiile/public", import.meta.url)), publicDir, { recursive: true })
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
if (localPublicDirExists) {
|
|
144
|
-
cpSync(localPublicDir, publicDir, { recursive: true })
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
if (argv.logo) {
|
|
148
|
-
try {
|
|
149
|
-
copyFileSync(path.resolve(source, argv.logo), path.resolve(publicDir, "favicon.png"))
|
|
150
|
-
// Set the logo to be displayed on the top bar if we were able to copy
|
|
151
|
-
process.env.VITE_COMPIIILE_LOGO = argv.logo
|
|
152
|
-
} catch (e) {
|
|
153
|
-
console.log(e)
|
|
154
|
-
console.error("Could not load provided logo: set a relative url from the current folder")
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
32
|
const require = createRequire(import.meta.url)
|
|
159
33
|
const pathName = require.resolve("@fontsource-variable/archivo")
|
|
160
34
|
|
|
@@ -164,13 +38,6 @@ if (packageDir) {
|
|
|
164
38
|
viteServerFsAllowList.push(packageDir)
|
|
165
39
|
}
|
|
166
40
|
|
|
167
|
-
const localIntegrations = []
|
|
168
|
-
|
|
169
|
-
if(/true/i.test(argv.printReady)){
|
|
170
|
-
const compiiilePrintIntegration = (await import("@compiiile/compiiile-print")).default
|
|
171
|
-
localIntegrations.push(compiiilePrintIntegration())
|
|
172
|
-
}
|
|
173
|
-
|
|
174
41
|
const astroConfig = {
|
|
175
42
|
server: {
|
|
176
43
|
host: argv.host,
|
|
@@ -241,12 +108,8 @@ const astroConfig = {
|
|
|
241
108
|
image: {
|
|
242
109
|
service: passthroughImageService()
|
|
243
110
|
},
|
|
244
|
-
...(configFromFile.astroConfig ?? {})
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
process.env.VITE_COMPIIILE_BASE = astroConfig.base
|
|
248
|
-
if (process.env.VITE_COMPIIILE_BASE !== "/" && process.env.VITE_COMPIIILE_BASE.endsWith("/")) {
|
|
249
|
-
process.env.VITE_COMPIIILE_BASE = process.env.VITE_COMPIIILE_BASE.slice(0, -1)
|
|
111
|
+
...(configFromFile.astroConfig ?? {}),
|
|
112
|
+
...(argv.astroConfig ?? {})
|
|
250
113
|
}
|
|
251
114
|
|
|
252
115
|
const run = async (astroConfig) => {
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises"
|
|
2
|
+
import { fileURLToPath } from "node:url"
|
|
3
|
+
import yargs from "yargs/yargs"
|
|
4
|
+
import { hideBin } from "yargs/helpers"
|
|
5
|
+
import path from "node:path"
|
|
6
|
+
import { copyFileSync, cpSync, existsSync, rmSync } from "node:fs"
|
|
7
|
+
import { loadConfig as loadConfigFile } from "c12"
|
|
8
|
+
|
|
9
|
+
export const loadConfig = async () => {
|
|
10
|
+
const source = process.cwd()
|
|
11
|
+
process.env.COMPIIILE_SOURCE = source
|
|
12
|
+
|
|
13
|
+
const packageJSON = JSON.parse(await readFile(fileURLToPath(new URL("../package.json", import.meta.url))))
|
|
14
|
+
|
|
15
|
+
/*
|
|
16
|
+
Order of options by priority:
|
|
17
|
+
1. command arguments
|
|
18
|
+
2. user-defined config in dedicated file
|
|
19
|
+
3. default config as fallback
|
|
20
|
+
*/
|
|
21
|
+
let configFromFile = {}
|
|
22
|
+
let compiiileConfig = {}
|
|
23
|
+
try {
|
|
24
|
+
compiiileConfig = await loadConfigFile({
|
|
25
|
+
name: process.env.COMPIIILE_TEMP_CONFIG_NAME || "compiiile",
|
|
26
|
+
cwd: process.env.COMPIIILE_TEMP_DIR || source
|
|
27
|
+
})
|
|
28
|
+
configFromFile = {...compiiileConfig.config}
|
|
29
|
+
if (!process.env.COMPIIILE_CONFIG_FILE) {
|
|
30
|
+
process.env.COMPIIILE_CONFIG_FILE = compiiileConfig.configFile
|
|
31
|
+
}
|
|
32
|
+
} catch {
|
|
33
|
+
// This means that no config file was provided: getting parameters from script parameters instead
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (process.env.COMPIIILE_TEMP_DIR) {
|
|
37
|
+
rmSync(process.env.COMPIIILE_TEMP_DIR, { recursive: true, force: true })
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (!configFromFile.astroConfig?.base) {
|
|
41
|
+
if (!configFromFile.astroConfig) {
|
|
42
|
+
configFromFile.astroConfig = {}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
configFromFile.astroConfig.base = "/"
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const argv = yargs(hideBin(process.argv))
|
|
49
|
+
.parserConfiguration({
|
|
50
|
+
"deep-merge-config": true
|
|
51
|
+
})
|
|
52
|
+
.alias("v", "version")
|
|
53
|
+
.config(configFromFile)
|
|
54
|
+
.command("dev", "launch development server")
|
|
55
|
+
.command("build", "build")
|
|
56
|
+
.command("preview", "preview")
|
|
57
|
+
.option("port", {
|
|
58
|
+
describe: "Port to use",
|
|
59
|
+
default: 4321
|
|
60
|
+
})
|
|
61
|
+
.option("host", {
|
|
62
|
+
describe: "Host to use",
|
|
63
|
+
default: "127.0.0.1"
|
|
64
|
+
})
|
|
65
|
+
.option("title", {
|
|
66
|
+
describe: "The title to display on the top-left of the User Interface"
|
|
67
|
+
})
|
|
68
|
+
.option("description", {
|
|
69
|
+
describe: "The description that is rendered by default for the SEO"
|
|
70
|
+
})
|
|
71
|
+
.option("logo", {
|
|
72
|
+
describe: "The relative path of the logo to display in the TopBar and as favicon"
|
|
73
|
+
})
|
|
74
|
+
.option("logoUrl", {
|
|
75
|
+
describe: "The url to go to when clicking on the logo, defaults to the home page if not set"
|
|
76
|
+
})
|
|
77
|
+
.option("dest", {
|
|
78
|
+
describe: "The folder in which to build files, defaults to `./.compiiile/dist`"
|
|
79
|
+
})
|
|
80
|
+
.option("siteUrl", {
|
|
81
|
+
describe: "The url of the website in production (without trailing slash), used for the SEO tag `og:image`"
|
|
82
|
+
})
|
|
83
|
+
.option("astroConfig", {
|
|
84
|
+
describe: "Override default Astro config (https://docs.astro.build/en/reference/configuration-reference/)"
|
|
85
|
+
})
|
|
86
|
+
.option("data", {
|
|
87
|
+
describe: "An object with data to use in MDX files"
|
|
88
|
+
})
|
|
89
|
+
.option("theme", {
|
|
90
|
+
describe:
|
|
91
|
+
"The website theme, value can be : `auto` (default value: adapts to system preferences) | `light` | `dark`"
|
|
92
|
+
})
|
|
93
|
+
.option("useAutoTitles", {
|
|
94
|
+
describe:
|
|
95
|
+
"If set to `true`, use the first file heading as title to be displayed in the navbar and for SEO. Defaults to `false`"
|
|
96
|
+
})
|
|
97
|
+
.option("noIndex", {
|
|
98
|
+
describe:
|
|
99
|
+
"If set to `true`, the `robots.txt` file will disallow all routes, preventing indexation. Defaults to `false`"
|
|
100
|
+
})
|
|
101
|
+
.option("publicDir", {
|
|
102
|
+
describe: "The folder name in which you can serve public files, defaults to `public`"
|
|
103
|
+
})
|
|
104
|
+
.option("vite.server.fs.allow", {
|
|
105
|
+
describe: "Add local paths to vite's server fs allow list"
|
|
106
|
+
})
|
|
107
|
+
.option("printReady", {
|
|
108
|
+
describe: "Add a /print page to display a full ready-to-print content (uses @compiiile/compiiile-print)"
|
|
109
|
+
})
|
|
110
|
+
.help()
|
|
111
|
+
.version(packageJSON.version).argv
|
|
112
|
+
|
|
113
|
+
process.env.VITE_COMPIIILE_SITE_URL = argv.siteUrl ?? ""
|
|
114
|
+
process.env.VITE_COMPIIILE_NO_INDEX = /true/i.test(argv.noIndex) // defaults to `false` if not set or not equal to `true`
|
|
115
|
+
|
|
116
|
+
process.env.VITE_COMPIIILE_TITLE = argv.title ?? ""
|
|
117
|
+
process.env.VITE_COMPIIILE_DESCRIPTION = argv.description ?? ""
|
|
118
|
+
|
|
119
|
+
process.env.VITE_COMPIIILE_LOGO_URL = argv.logoUrl ?? ""
|
|
120
|
+
|
|
121
|
+
process.env.VITE_COMPIIILE_THEME = argv.theme ?? "auto"
|
|
122
|
+
|
|
123
|
+
process.env.VITE_COMPIIILE_DATA = typeof argv.data === "string" ? argv.data : JSON.stringify(argv.data ?? {})
|
|
124
|
+
process.env.VITE_COMPIIILE_USE_AUTO_TITLES = /true/i.test(argv.useAutoTitles) // defaults to `false` if not set or not equal to `true`
|
|
125
|
+
|
|
126
|
+
// Handling logo and favicon
|
|
127
|
+
process.env.VITE_COMPIIILE_LOGO = null
|
|
128
|
+
|
|
129
|
+
const publicDir = path.resolve(source, "./.compiiile/public")
|
|
130
|
+
|
|
131
|
+
const localPublicDirName = argv.publicDir ?? "public"
|
|
132
|
+
const localPublicDir = path.resolve(source, localPublicDirName)
|
|
133
|
+
const localPublicDirExists = existsSync(localPublicDir)
|
|
134
|
+
|
|
135
|
+
const hasPublicFiles = localPublicDirExists || argv.logo
|
|
136
|
+
if (hasPublicFiles) {
|
|
137
|
+
cpSync(fileURLToPath(new URL("../.compiiile/public", import.meta.url)), publicDir, { recursive: true })
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (localPublicDirExists) {
|
|
141
|
+
cpSync(localPublicDir, publicDir, { recursive: true })
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (argv.logo) {
|
|
145
|
+
try {
|
|
146
|
+
copyFileSync(path.resolve(source, argv.logo), path.resolve(publicDir, "favicon.png"))
|
|
147
|
+
// Set the logo to be displayed on the top bar if we were able to copy
|
|
148
|
+
process.env.VITE_COMPIIILE_LOGO = argv.logo
|
|
149
|
+
} catch (e) {
|
|
150
|
+
console.log(e)
|
|
151
|
+
console.error("Could not load provided logo: set a relative url from the current folder")
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const localIntegrations = []
|
|
156
|
+
|
|
157
|
+
if (/true/i.test(argv.printReady)) {
|
|
158
|
+
const compiiilePrintIntegration = (await import("@compiiile/compiiile-print")).default
|
|
159
|
+
localIntegrations.push(compiiilePrintIntegration())
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
process.env.VITE_COMPIIILE_BASE = argv.astroConfig.base
|
|
163
|
+
if (process.env.VITE_COMPIIILE_BASE !== "/" && process.env.VITE_COMPIIILE_BASE.endsWith("/")) {
|
|
164
|
+
process.env.VITE_COMPIIILE_BASE = process.env.VITE_COMPIIILE_BASE.slice(0, -1)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return {
|
|
168
|
+
argv,
|
|
169
|
+
localIntegrations,
|
|
170
|
+
configFromFile,
|
|
171
|
+
source,
|
|
172
|
+
hasPublicFiles,
|
|
173
|
+
publicDir
|
|
174
|
+
}
|
|
175
|
+
}
|
|
@@ -1,7 +1,18 @@
|
|
|
1
1
|
import Context from "./models/Context.js"
|
|
2
|
+
import { createMarkdownProcessor } from "@astrojs/markdown-remark"
|
|
3
|
+
import markdownConfig from "./markdownConfig.js"
|
|
4
|
+
import path from "node:path"
|
|
5
|
+
import { loadConfig } from "../loadConfig.js"
|
|
6
|
+
import { promises as fs } from "node:fs"
|
|
2
7
|
|
|
3
8
|
const source = "."
|
|
4
9
|
|
|
10
|
+
let context = null
|
|
11
|
+
|
|
12
|
+
const pathFromSource = (filePath) => {
|
|
13
|
+
return filePath?.replace(process.env.COMPIIILE_SOURCE + "/", "")
|
|
14
|
+
}
|
|
15
|
+
|
|
5
16
|
export default function compiiile() {
|
|
6
17
|
const virtualModuleId = "virtual:compiiile"
|
|
7
18
|
const resolvedVirtualModuleId = "\0" + virtualModuleId
|
|
@@ -18,7 +29,7 @@ export default function compiiile() {
|
|
|
18
29
|
return
|
|
19
30
|
}
|
|
20
31
|
|
|
21
|
-
|
|
32
|
+
context = new Context()
|
|
22
33
|
context.filesTree = await context.scanDirectoryRecursively(source)
|
|
23
34
|
process.env.context = JSON.stringify(context)
|
|
24
35
|
|
|
@@ -27,6 +38,96 @@ export default function compiiile() {
|
|
|
27
38
|
const routeList = ${JSON.stringify(context.routeList)};\n\n
|
|
28
39
|
const site = ${JSON.stringify(context.site)};\n\n
|
|
29
40
|
export { fileList, filesTree, routeList, site };`
|
|
41
|
+
},
|
|
42
|
+
async hotUpdate({ file, read }) {
|
|
43
|
+
let shouldReloadPlugin = false
|
|
44
|
+
const absolutePath = pathFromSource(file)
|
|
45
|
+
|
|
46
|
+
if (file.match(/.*\.mdx?/)) {
|
|
47
|
+
try {
|
|
48
|
+
const content = await read()
|
|
49
|
+
|
|
50
|
+
const routeListItem = context.routeList.find((route) => route.fullPath === absolutePath)
|
|
51
|
+
|
|
52
|
+
const markdownProcessor = await createMarkdownProcessor(markdownConfig)
|
|
53
|
+
const renderedMarkdown = await markdownProcessor.render(content)
|
|
54
|
+
|
|
55
|
+
const title = context.getFileTitleFromProcessedMarkdown(renderedMarkdown)
|
|
56
|
+
const meta = renderedMarkdown.metadata.frontmatter
|
|
57
|
+
meta.title = title || path.parse(file).name
|
|
58
|
+
|
|
59
|
+
const fileMetaChanged = JSON.stringify(routeListItem?.meta || {}) !== JSON.stringify(meta)
|
|
60
|
+
|
|
61
|
+
shouldReloadPlugin = fileMetaChanged || !routeListItem
|
|
62
|
+
|
|
63
|
+
const prevStateWasAsSlides = !!routeListItem?.meta?.asSlides
|
|
64
|
+
const currentStateIsAsSlides = !!meta.asSlides
|
|
65
|
+
|
|
66
|
+
if (prevStateWasAsSlides !== currentStateIsAsSlides) {
|
|
67
|
+
const newRoutePath = context.generateRoutePathFromFilePath(
|
|
68
|
+
routeListItem.fullPath,
|
|
69
|
+
"",
|
|
70
|
+
currentStateIsAsSlides,
|
|
71
|
+
context.getEntryFileMatcher([routeListItem.fullPath])
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
this.environment.hot.send({
|
|
75
|
+
type: "custom",
|
|
76
|
+
event: "switch-page-render",
|
|
77
|
+
data: {
|
|
78
|
+
oldRoutePath: routeListItem.path,
|
|
79
|
+
newRoutePath: newRoutePath
|
|
80
|
+
}
|
|
81
|
+
})
|
|
82
|
+
}
|
|
83
|
+
} catch (e) {
|
|
84
|
+
if (e.code === "ENOENT") {
|
|
85
|
+
// The file has been deleted
|
|
86
|
+
shouldReloadPlugin = true
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const compiiileConfigFilePath = pathFromSource(process.env.COMPIIILE_CONFIG_FILE)
|
|
92
|
+
const compiiileConfigFileNewlyCreated =
|
|
93
|
+
absolutePath.match(/^compiiile\.config\.(m|c)?js$/) &&
|
|
94
|
+
process.env.COMPIIILE_CONFIG_FILE === "compiiile.config"
|
|
95
|
+
|
|
96
|
+
if (absolutePath === compiiileConfigFilePath || compiiileConfigFileNewlyCreated) {
|
|
97
|
+
shouldReloadPlugin = true
|
|
98
|
+
|
|
99
|
+
// WHY we do all this stuff and don't just import the config:
|
|
100
|
+
// "the file change callback may fire too fast before the editor finishes updating the file"
|
|
101
|
+
// https://vite.dev/guide/api-plugin.html#handlehotupdate
|
|
102
|
+
|
|
103
|
+
try {
|
|
104
|
+
const compiiileConfigFileContent = await read()
|
|
105
|
+
const tempConfigName = `${Date.now()}-temp-compiiile`
|
|
106
|
+
const tempDir = path.join(process.env.COMPIIILE_SOURCE, ".compiiile", ".temp")
|
|
107
|
+
await fs.mkdir(tempDir, { recursive: true }).catch(console.error)
|
|
108
|
+
const filePath = path.join(tempDir, tempConfigName + ".config.mjs")
|
|
109
|
+
await fs.writeFile(filePath, compiiileConfigFileContent)
|
|
110
|
+
process.env.COMPIIILE_TEMP_DIR = tempDir
|
|
111
|
+
process.env.COMPIIILE_TEMP_CONFIG_NAME = tempConfigName
|
|
112
|
+
} catch (e) {
|
|
113
|
+
// Config file has been deleted
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Whether the config file has been created / updated / deleted, we reload the whole config with args + env parameters
|
|
117
|
+
await loadConfig()
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (shouldReloadPlugin) {
|
|
121
|
+
// Invalidate the plugin module so it gets re-imported
|
|
122
|
+
const pluginModule = this.environment.moduleGraph.getModuleById(resolvedVirtualModuleId)
|
|
123
|
+
if (pluginModule) {
|
|
124
|
+
this.environment.moduleGraph.invalidateModule(pluginModule)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
this.environment.hot.send({ type: "full-reload" })
|
|
128
|
+
|
|
129
|
+
return []
|
|
130
|
+
}
|
|
30
131
|
}
|
|
31
132
|
}
|
|
32
133
|
}
|
|
@@ -55,6 +55,25 @@ export default class {
|
|
|
55
55
|
}/${asSlides ? this.SLIDES_BASE_PATH : this.WORKSPACE_BASE_PATH}/${sluggifiedPath}${hash}`
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
getFileTitleFromProcessedMarkdown(processedMarkdown) {
|
|
59
|
+
let firstHeading = null
|
|
60
|
+
if (JSON.parse(process.env.VITE_COMPIIILE_USE_AUTO_TITLES) && processedMarkdown.metadata.headings.length > 0) {
|
|
61
|
+
let firstHeadingIndex = 0
|
|
62
|
+
if (Object.keys(processedMarkdown.metadata.frontmatter).length > 0) {
|
|
63
|
+
// If a frontmatter is set, it is present as the first index in the `headings` array
|
|
64
|
+
firstHeadingIndex = 1
|
|
65
|
+
}
|
|
66
|
+
// Remove the starting '#' from the title
|
|
67
|
+
firstHeading = processedMarkdown.metadata.headings[firstHeadingIndex]?.text?.slice(1)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return processedMarkdown.metadata.frontmatter.title || firstHeading
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
getEntryFileMatcher(files) {
|
|
74
|
+
return files.find((file) => file.toLowerCase().match(/^readme.mdx?$/)) ? /readme/ : /index/
|
|
75
|
+
}
|
|
76
|
+
|
|
58
77
|
async scanDirectoryRecursively(directoryPath) {
|
|
59
78
|
const fileArray = []
|
|
60
79
|
|
|
@@ -65,7 +84,7 @@ export default class {
|
|
|
65
84
|
|
|
66
85
|
const files = fs.readdirSync(directoryPath).sort(collator.compare)
|
|
67
86
|
|
|
68
|
-
const entryFileMatcher =
|
|
87
|
+
const entryFileMatcher = this.getEntryFileMatcher(files)
|
|
69
88
|
|
|
70
89
|
for (let file of files) {
|
|
71
90
|
if (
|
|
@@ -153,23 +172,10 @@ export default class {
|
|
|
153
172
|
continue
|
|
154
173
|
}
|
|
155
174
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
JSON.parse(process.env.VITE_COMPIIILE_USE_AUTO_TITLES) &&
|
|
159
|
-
renderedMarkdown.metadata.headings.length > 0
|
|
160
|
-
) {
|
|
161
|
-
let firstHeadingIndex = 0
|
|
162
|
-
if (Object.keys(renderedMarkdown.metadata.frontmatter).length > 0) {
|
|
163
|
-
// If a frontmatter is set, it is present as the first index in the `headings` array
|
|
164
|
-
firstHeadingIndex = 1
|
|
165
|
-
}
|
|
166
|
-
// Remove the starting '#' from the title
|
|
167
|
-
firstHeading = renderedMarkdown.metadata.headings[firstHeadingIndex]?.text?.slice(1)
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
fileListItem.title = meta.title || firstHeading || fileName
|
|
175
|
+
const title = this.getFileTitleFromProcessedMarkdown(renderedMarkdown) || fileName
|
|
176
|
+
fileListItem.title = title
|
|
171
177
|
fileListItem.meta = meta
|
|
172
|
-
fileListItem.meta.title =
|
|
178
|
+
fileListItem.meta.title = title
|
|
173
179
|
fileListItem.fullPath = filePath
|
|
174
180
|
|
|
175
181
|
const routePath = this.generateRoutePathFromFilePath(
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@compiiile/compiiile",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.17.1",
|
|
5
5
|
"description": "The most convenient way to render a folder containing markdown files. Previewing and searching markdown files has never been that easy.",
|
|
6
6
|
"author": "AlbanCrepel <alban.crepel@gmail.com>",
|
|
7
7
|
"license": "GPL-3.0-only",
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
/* eslint-disable */
|
|
2
|
-
// @ts-nocheck
|
|
3
|
-
// Generated by unplugin-vue-components
|
|
4
|
-
// Read more: https://github.com/vuejs/core/pull/3399
|
|
5
|
-
export {}
|
|
6
|
-
|
|
7
|
-
/* prettier-ignore */
|
|
8
|
-
declare module 'vue' {
|
|
9
|
-
export interface GlobalComponents {
|
|
10
|
-
ClientScript: typeof import('./src/components/ClientScript.vue')['default']
|
|
11
|
-
ContentWrapper: typeof import('./src/components/ContentWrapper.vue')['default']
|
|
12
|
-
FilesTree: typeof import('./src/components/layout/navBar/FilesTree.vue')['default']
|
|
13
|
-
HamburgerButton: typeof import('./src/components/layout/HamburgerButton.vue')['default']
|
|
14
|
-
NavBar: typeof import('./src/components/layout/navBar/NavBar.vue')['default']
|
|
15
|
-
NavListItem: typeof import('./src/components/layout/navBar/NavListItem.vue')['default']
|
|
16
|
-
SearchBar: typeof import('./src/components/searchBar/SearchBar.vue')['default']
|
|
17
|
-
SearchResult: typeof import('./src/components/searchBar/SearchResult.vue')['default']
|
|
18
|
-
SlidesContent: typeof import('./src/components/SlidesContent.vue')['default']
|
|
19
|
-
TableOfContent: typeof import('./src/components/TableOfContent.vue')['default']
|
|
20
|
-
ThemeSwitcher: typeof import('./src/components/layout/ThemeSwitcher.vue')['default']
|
|
21
|
-
TopBar: typeof import('./src/components/layout/TopBar.vue')['default']
|
|
22
|
-
}
|
|
23
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
/// <reference types="astro/client" />
|