@compiiile/compiiile 2.16.0 → 2.17.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.
@@ -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>
@@ -21,7 +21,7 @@ html {
21
21
  -webkit-font-smoothing: antialiased;
22
22
  box-sizing: border-box;
23
23
  background-color: var(--layout-background-color);
24
- font-family: "DM Sans Variable", sans-serif;
24
+ font-family: var(--default-font);
25
25
  }
26
26
 
27
27
  #__nuxt,
@@ -9,7 +9,7 @@
9
9
  .reveal {
10
10
  height: 100vh;
11
11
  width: 100vw;
12
- font-family: "DM Sans Variable", sans-serif !important;
12
+ font-family: var(--default-font) !important;
13
13
 
14
14
  .slides {
15
15
  text-align: inherit;
@@ -39,7 +39,7 @@
39
39
  h4,
40
40
  h5,
41
41
  h6 {
42
- font-family: "Archivo Variable", sans-serif;
42
+ font-family: var(--heading-font);
43
43
  font-variation-settings:
44
44
  "wght" 900,
45
45
  "wdth" 125;
@@ -3,7 +3,7 @@ h2,
3
3
  h3,
4
4
  h4,
5
5
  h5 {
6
- font-family: "Archivo Variable", sans-serif;
6
+ font-family: var(--heading-font);
7
7
  margin: 3rem 0 1.38rem;
8
8
  line-height: 1;
9
9
  font-variation-settings: "wght" 900;
@@ -87,3 +87,7 @@ body {
87
87
  .hidden {
88
88
  visibility: hidden;
89
89
  }
90
+
91
+ details summary {
92
+ cursor: pointer;
93
+ }
@@ -8,6 +8,8 @@
8
8
  --ease-in-out-quart: cubic-bezier(0.77, 0, 0.175, 1);
9
9
  --monospace: "JetBrains Mono Variable", monospace;
10
10
  --r-code-font: "JetBrains Mono Variable", monospace;
11
+ --heading-font: "Archivo Variable", sans-serif;
12
+ --default-font: "DM Sans Variable", sans-serif;
11
13
 
12
14
  /* Dark theme by default */
13
15
  --layout-background-color: #1b1b1f;
@@ -5,6 +5,30 @@ description: "Use icons, admonitions, mermaid diagrams, markmap mindmaps in Mark
5
5
 
6
6
  # compiiile-pro added features <Icon name="star" />
7
7
 
8
+ ## Customize style by providing your own CSS file
9
+
10
+ You can customize every bit of the UI with your own style by passing a `css` param with the relative path to your css file :
11
+
12
+ ```bash
13
+ compiiile-pro dev --css="./custom.css"
14
+ ```
15
+
16
+ Here is an example of a CSS file content to set some colors and the default font:
17
+
18
+ ```css
19
+ @import url(https://fonts.bunny.net/css?family=inter:200i,400,400i,900);
20
+
21
+ :root {
22
+ --layout-background-color: #0000ff;
23
+ --darker-background-color: #00ff00;
24
+ --default-font: "Inter", sans-serif;
25
+ }
26
+ ```
27
+
28
+ :nail_care: The list of global CSS variables can be found here : https://github.com/compiiile/compiiile/blob/master/.compiiile/src/style/variables.css
29
+
30
+ :rainbow: You can also just target CSS classes and tweak them the way you want.
31
+
8
32
  ## Admonitions
9
33
 
10
34
  > [!NOTE]
@@ -572,7 +596,7 @@ The `CCard` component can be used as a traditional card or a link.
572
596
  <details>
573
597
  <summary>Source</summary>
574
598
 
575
- ````md
599
+ ````mdx
576
600
  <CGrid gap="20px" template="1 1">
577
601
  <CCard hintText="Go to the specific page">
578
602
  [Check compiiile-pro installation](./3-pro-installation.md)
@@ -590,3 +614,23 @@ The `CCard` component can be used as a traditional card or a link.
590
614
  ````
591
615
 
592
616
  </details>
617
+
618
+ ### CDetails
619
+
620
+ The `CDetails` component allows you to hide a block and reveal it by clicking on an arrow, just like the `details` HTML tag.
621
+
622
+ - The title can be set via a `summary` prop or a `summary` slot
623
+
624
+ <CDetails summary="Example">
625
+ > [!TIP]
626
+ > This is lit! :sparkles:
627
+ </CDetails>
628
+
629
+ <CDetails summary="Source">
630
+ ```mdx
631
+ <CDetails summary="Example">
632
+ > [!TIP]
633
+ > This is lit! :sparkles:
634
+ </CDetails>
635
+ ```
636
+ </CDetails>
package/README.md CHANGED
@@ -197,7 +197,7 @@ The home page of Compiiile (`/`) points to a `README.md` file located at the roo
197
197
  Here is the list of parameters that you can set to customize Compiiile (none are required):
198
198
 
199
199
  | Parameter | Type | Description |
200
- |------------------------|------------|----------------------------------------------------------------------------------------------------------------------|
200
+ | ---------------------- | ---------- | -------------------------------------------------------------------------------------------------------------------- |
201
201
  | `title` | `string` | The title to display on the top-left of the User Interface |
202
202
  | `description` | `string` | The description that is rendered by default for the SEO |
203
203
  | `logo` | `string` | The relative path of the logo to display in the TopBar and as favicon |
@@ -212,6 +212,7 @@ Here is the list of parameters that you can set to customize Compiiile (none are
212
212
  | `publicDir` | `string` | The folder name in which you can serve public files, defaults to `public` |
213
213
  | `vite.server.fs.allow` | `string[]` | Add local paths to vite's server fs allow list |
214
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` |
215
216
 
216
217
  You can use these parameters in 2 ways:
217
218
 
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
- const source = process.cwd()
21
- process.env.COMPIIILE_SOURCE = source
15
+ import { loadConfig } from "./loadConfig.js"
22
16
 
23
- const packageJSON = JSON.parse(await readFile(fileURLToPath(new URL("../package.json", import.meta.url))))
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 = JSON.parse(JSON.stringify(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
- const context = new Context()
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 = files.find((file) => file.toLowerCase().match(/^readme.mdx?$/)) ? /readme/ : /index/
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
- let firstHeading = null
157
- if (
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 = fileListItem.meta.title || fileListItem.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.16.0",
4
+ "version": "2.17.0",
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",
@@ -29,7 +29,7 @@
29
29
  "@babel/eslint-parser": "^7.25.9",
30
30
  "@babel/parser": "^7.26.3",
31
31
  "@compiiile/compiiile-print": "^1.0.6",
32
- "@compiiile/compiiile-pro": "^1.1.9",
32
+ "@compiiile/compiiile-pro": "^1.2.0",
33
33
  "@eslint/compat": "^1.2.4",
34
34
  "@eslint/js": "^9.16.0",
35
35
  "@fontsource-variable/archivo": "^5.1.0",
@@ -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" />