moonwave-gitlab 1.3.3

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.
@@ -0,0 +1,492 @@
1
+ import cachedir from "cachedir"
2
+ import { execSync } from "child_process"
3
+ import fs from "fs-extra"
4
+ import parseGitConfig from "parse-git-config"
5
+ import path, { dirname } from "path"
6
+ import toml from "toml"
7
+ import { fileURLToPath } from "url"
8
+ import getDocusaurusConfig, {
9
+ GenerateConfigParams,
10
+ } from "./getDocusaurusConfig.js"
11
+
12
+ const __dirname = dirname(fileURLToPath(import.meta.url))
13
+
14
+ const TEMPLATE_PATH = path.join(__dirname, "../template")
15
+ const ROOT_PATH = path.join(TEMPLATE_PATH, "root")
16
+
17
+ const SNIP_BEFORE = "<!--moonwave-hide-before-this-line-->"
18
+ const SNIP_AFTER = "<!--moonwave-hide-after-this-line-->"
19
+ const INDEX_EXTS = ["html", "js", "mdx", "md"]
20
+ const COPY_FOLDERS = ["blog", "docs", "pages"] as const
21
+
22
+ const NO_README_TEXT = (title: string) => `# ${title}
23
+ This project doesn't have a README.
24
+ If it had a README.md in its root directory, you would be reading that right now.`
25
+
26
+ const NO_GIT_REPO_TEXT = `# This project has no configured title
27
+ The site title is usually pulled from your Git repo, but no git repo could be detected.
28
+ Either set this project up as a Git repo, or configure the website title in moonwave.toml`
29
+
30
+ export type FoldersEnabled = {
31
+ [index in (typeof COPY_FOLDERS)[number]]: boolean
32
+ }
33
+
34
+ export type ClassOrder = (
35
+ | string
36
+ | {
37
+ section?: string
38
+ classes?: string[]
39
+ tag?: string
40
+ }
41
+ )[]
42
+
43
+ export type Config = Partial<{
44
+ // Moonwave
45
+ gitRepoUrl: string
46
+ gitSourceBranch: string
47
+ title: string
48
+ changelog: boolean
49
+ classOrder: ClassOrder
50
+ apiCategories: string[]
51
+ autoSectionPath?: string
52
+
53
+ // Docusaurus
54
+ docusaurus: Partial<{
55
+ title: string
56
+ tagline: string
57
+ url: string
58
+ baseUrl: string
59
+ onBrokenLinks: string
60
+ onBrokenMarkdownLinks: string
61
+ favicon: string
62
+ organizationName: string
63
+ projectName: string
64
+ }>
65
+
66
+ navbar: Partial<{
67
+ title: string
68
+ logo: { alt: string; src: string }
69
+ items: { to: string; label: string; position: "left" | "right" }[]
70
+ }>
71
+
72
+ home: Partial<{
73
+ enabled: boolean
74
+ bannerImage: string
75
+ includeReadme: boolean
76
+
77
+ features: {
78
+ title: string
79
+ description: string
80
+ image: string
81
+ }[]
82
+ }>
83
+
84
+ footer: Partial<{
85
+ style: string
86
+ links: {
87
+ title: string
88
+ copyright: string
89
+ items: { label: string; to: string }[]
90
+ }[]
91
+ }>
92
+ }>
93
+
94
+ function getGitRepoUrl(): string | undefined {
95
+ const gitConfig = parseGitConfig.sync()
96
+
97
+ if (gitConfig) {
98
+ if (gitConfig['remote "origin"']?.url?.includes("git@")) {
99
+ const [, repoHostSite, repoAuthor, repoName] = gitConfig[
100
+ 'remote "origin"'
101
+ ]?.url
102
+ .replace(/\.git$/, "")
103
+ .match(/^git@+(.+):(.+)\/(.+)$/)
104
+ return `https://${repoHostSite}/${repoAuthor}/${repoName}`
105
+ } else {
106
+ return gitConfig['remote "origin"']?.url
107
+ ?.replace(/\.git$/, "")
108
+ ?.replace(/\/\/.*@/, "//") // Strip out http basic auth if present
109
+ }
110
+ }
111
+ }
112
+
113
+ function readConfig(projectDir: string): Config {
114
+ const configPath = path.join(projectDir, "moonwave")
115
+
116
+ if (fs.existsSync(configPath + ".toml")) {
117
+ return toml.parse(
118
+ fs.readFileSync(configPath + ".toml", { encoding: "utf-8" })
119
+ )
120
+ } else if (fs.existsSync(configPath + ".json")) {
121
+ return fs.readJSONSync(configPath + ".json")
122
+ }
123
+
124
+ return {}
125
+ }
126
+
127
+ function getConfig(projectDir: string): Config {
128
+ const gitRepoUrl = getGitRepoUrl()
129
+
130
+ const [, repoAuthor, repoName] =
131
+ gitRepoUrl?.match(/^https?:\/\/.+\/(.+)\/(.+)$/) || []
132
+
133
+ const config = readConfig(projectDir)
134
+
135
+ // Note: Only copying values from other places in the config should go here.
136
+ // Default values for docusaurus.config.js belong in getDocusaurusConfig
137
+ return {
138
+ title: repoName,
139
+ gitRepoUrl: gitRepoUrl,
140
+ changelog: true,
141
+ ...config,
142
+
143
+ docusaurus: {
144
+ projectName: repoName ?? undefined,
145
+ organizationName: repoAuthor ?? undefined,
146
+ title: config.title ?? repoName ?? "You need to configure your title",
147
+ baseUrl: repoName ? `/${repoName}/` : "/",
148
+ ...config.docusaurus,
149
+ },
150
+
151
+ navbar: {
152
+ title: config.title ?? config.docusaurus?.title ?? repoName ?? "No Title",
153
+ ...config.navbar,
154
+ },
155
+ }
156
+ }
157
+
158
+ function prepareReadme(readmeContent: string): string {
159
+ while (true) {
160
+ const index_before = readmeContent.indexOf(SNIP_BEFORE)
161
+ const index_after = readmeContent.indexOf(SNIP_AFTER)
162
+
163
+ if (index_before > -1) {
164
+ if (index_after > -1 && index_after < index_before) {
165
+ // snip text between comments
166
+ readmeContent =
167
+ readmeContent.slice(0, index_after) +
168
+ readmeContent.slice(index_before + SNIP_BEFORE.length)
169
+ } else {
170
+ // regular hide before
171
+ readmeContent = readmeContent.slice(index_before + SNIP_BEFORE.length)
172
+ }
173
+ } else if (index_after > -1) {
174
+ // can only handle a lone hide-after if no hide-before
175
+ readmeContent = readmeContent.slice(0, index_after)
176
+ } else {
177
+ return readmeContent
178
+ }
179
+ }
180
+ }
181
+
182
+ function makeHomePage(projectDir: string, tempDir: string, config: Config) {
183
+ if (
184
+ INDEX_EXTS.filter((ext) =>
185
+ fs.existsSync(path.join(projectDir, "pages", "index." + ext))
186
+ ).length === 0
187
+ ) {
188
+ fs.ensureDirSync(path.join(tempDir, "pages"))
189
+
190
+ INDEX_EXTS.forEach((ext) =>
191
+ fs.removeSync(path.join(tempDir, "pages", "index." + ext))
192
+ )
193
+
194
+ if (config.home?.enabled) {
195
+ const features = config.home?.features?.map((feature) => {
196
+ if (feature.image && feature.image.startsWith("/")) {
197
+ feature.image = config.docusaurus?.baseUrl + feature.image
198
+ }
199
+
200
+ return feature
201
+ })
202
+
203
+ let indexSource = fs
204
+ .readFileSync(path.join(TEMPLATE_PATH, "home", "index.js"), {
205
+ encoding: "utf-8",
206
+ })
207
+ .replace("/***features***/", JSON.stringify(features ?? null))
208
+
209
+ const readmePath = path.join(
210
+ projectDir,
211
+ typeof config.home?.includeReadme === "string"
212
+ ? config.home.includeReadme
213
+ : "README.md"
214
+ )
215
+ if (config.home?.includeReadme && fs.existsSync(readmePath)) {
216
+ fs.copyFileSync(readmePath, path.join(tempDir, "README.md"))
217
+
218
+ let readmeContent = fs.readFileSync(readmePath, { encoding: "utf-8" })
219
+
220
+ readmeContent = prepareReadme(readmeContent)
221
+
222
+ fs.writeFileSync(path.join(tempDir, "README.md"), readmeContent)
223
+ indexSource = 'import README from "../README.md"\n' + indexSource
224
+ indexSource = indexSource.replace("{/***readme***/}", "<README />")
225
+ }
226
+
227
+ fs.writeFileSync(path.join(tempDir, "pages", "index.js"), indexSource)
228
+
229
+ fs.copyFileSync(
230
+ path.join(TEMPLATE_PATH, "home", "index.module.css"),
231
+ path.join(tempDir, "pages", "index.module.css")
232
+ )
233
+ } else {
234
+ const indexPath = path.join(tempDir, "pages", "index.md")
235
+ const readmePath = path.join(projectDir, "README.md")
236
+
237
+ if (fs.existsSync(readmePath)) {
238
+ let readmeContent = fs.readFileSync(readmePath, { encoding: "utf-8" })
239
+
240
+ readmeContent = prepareReadme(readmeContent)
241
+
242
+ fs.writeFileSync(indexPath, readmeContent)
243
+ } else {
244
+ const placeholderHomeText = config.title
245
+ ? NO_README_TEXT(config.title)
246
+ : NO_GIT_REPO_TEXT
247
+
248
+ fs.writeFileSync(indexPath, placeholderHomeText)
249
+ }
250
+ }
251
+ }
252
+ }
253
+
254
+ function copyChangelog(
255
+ projectDir: string,
256
+ tempDir: string,
257
+ config: Config
258
+ ): boolean {
259
+ const changelogPath = path.join(projectDir, "CHANGELOG.md")
260
+ const targetPath = path.join(tempDir, "pages", "changelog.md")
261
+
262
+ if (config.changelog && fs.existsSync(changelogPath)) {
263
+ fs.ensureDirSync(path.join(tempDir, "pages"))
264
+
265
+ fs.copyFileSync(changelogPath, targetPath)
266
+
267
+ return true
268
+ } else if (fs.existsSync(targetPath)) {
269
+ fs.removeSync(targetPath)
270
+ }
271
+
272
+ return false
273
+ }
274
+
275
+ function copyMoonwaveFolder(
276
+ projectDir: string,
277
+ tempDir: string
278
+ ): { customCssExists: boolean; customSidebarExists: boolean } {
279
+ const staticDir = path.join(projectDir, ".moonwave", "static")
280
+ if (fs.existsSync(staticDir)) {
281
+ fs.copySync(staticDir, path.join(tempDir, "static"))
282
+ }
283
+
284
+ const status = { customCssExists: false, customSidebarExists: false }
285
+
286
+ const customCssPath = path.join(projectDir, ".moonwave", "custom.css")
287
+ if (fs.existsSync(customCssPath)) {
288
+ fs.copySync(customCssPath, path.join(tempDir, "src", "css", "custom.css"))
289
+
290
+ status.customCssExists = true
291
+ }
292
+
293
+ const customSidebarsPath = path.join(projectDir, ".moonwave", "sidebars.js")
294
+ if (fs.existsSync(customSidebarsPath)) {
295
+ fs.copySync(customSidebarsPath, path.join(tempDir, "src", "sidebars.js"))
296
+
297
+ status.customSidebarExists = true
298
+ }
299
+
300
+ return status
301
+ }
302
+
303
+ function writeDocusaurusConfig(tempDir: string, params: GenerateConfigParams) {
304
+ const docusaurusConfigPath = path.join(tempDir, "./docusaurus.config.js")
305
+ const newDocusaurusConfig =
306
+ "module.exports = " + JSON.stringify(getDocusaurusConfig(params), null, 2)
307
+
308
+ if (
309
+ fs.existsSync(docusaurusConfigPath) &&
310
+ fs.readFileSync(docusaurusConfigPath, { encoding: "utf-8" }) ===
311
+ newDocusaurusConfig
312
+ ) {
313
+ return false
314
+ } else {
315
+ fs.writeFileSync(docusaurusConfigPath, newDocusaurusConfig)
316
+ return true
317
+ }
318
+ }
319
+
320
+ function copyContentFolders(
321
+ projectDir: string,
322
+ tempDir: string
323
+ ): FoldersEnabled {
324
+ return Object.fromEntries(
325
+ COPY_FOLDERS.map((folder) => {
326
+ const folderPath = path.join(projectDir, folder)
327
+ const targetPath = path.join(tempDir, folder)
328
+
329
+ if (fs.existsSync(folderPath)) {
330
+ fs.copySync(folderPath, targetPath)
331
+ return true
332
+ } else {
333
+ return false
334
+ }
335
+ }).map((wasFound, index) => [COPY_FOLDERS[index], wasFound])
336
+ ) as FoldersEnabled
337
+ }
338
+
339
+ function needsCompleteRebuild(tempDir: string): boolean {
340
+ if (process.env.MOONWAVE_DEV) {
341
+ // We do fancy things to package.json in dev mode, which causes this code to always think a rebuild is needed
342
+ return false
343
+ }
344
+
345
+ if (
346
+ !fs.existsSync(tempDir) ||
347
+ !fs.existsSync(path.join(tempDir, "package.json")) ||
348
+ !fs.existsSync(path.join(tempDir, "package-lock.json"))
349
+ ) {
350
+ console.log(
351
+ "Moonwave: package.json or package-lock.json does not exist, rebuilding..."
352
+ )
353
+ return true
354
+ }
355
+
356
+ if (
357
+ !fs
358
+ .readFileSync(path.join(ROOT_PATH, "package.json"))
359
+ .equals(fs.readFileSync(path.join(tempDir, "package.json")))
360
+ ) {
361
+ console.log(
362
+ "Moonwave: package.json differs from cached files, rebuilding..."
363
+ )
364
+ return true
365
+ }
366
+
367
+ return false
368
+ }
369
+
370
+ export interface PreparedProject {
371
+ tempDir: string
372
+ projectDir: string
373
+
374
+ watchPaths: string[]
375
+
376
+ docusaurusConfigModified: boolean
377
+ }
378
+
379
+ export interface PrepareProjectOptions {
380
+ codePaths: string[]
381
+ binaryPath: string
382
+ skipRootCopy?: boolean
383
+ fresh?: boolean
384
+ install?: boolean
385
+ }
386
+
387
+ export function prepareProject(
388
+ projectDir: string,
389
+ options: PrepareProjectOptions
390
+ ): PreparedProject {
391
+ const config = getConfig(projectDir)
392
+
393
+ const folderName = projectDir.split(path.sep).slice(-1)[0] ?? "unknown"
394
+ const tempDir = path.join(cachedir("moonwave"), folderName)
395
+
396
+ if (
397
+ (options.install && fs.existsSync(tempDir)) ||
398
+ needsCompleteRebuild(tempDir)
399
+ ) {
400
+ console.log(`Deleting ${tempDir} for complete re-install`)
401
+ fs.removeSync(tempDir)
402
+ } else if (options.fresh && fs.existsSync(tempDir)) {
403
+ for (const file of fs
404
+ .readdirSync(tempDir)
405
+ .filter((name) => name !== "node_modules")) {
406
+ fs.removeSync(path.join(tempDir, file))
407
+ }
408
+ }
409
+
410
+ if (!options.skipRootCopy) {
411
+ fs.copySync(ROOT_PATH, tempDir)
412
+
413
+ const moonwavePluginPath = process.env.MOONWAVE_PLUGIN_PATH
414
+
415
+ if (process.env.MOONWAVE_DEV || moonwavePluginPath) {
416
+ console.log(
417
+ `Moonwave: Using development Docusaurus plugin: ${
418
+ process.env.MOONWAVE_PLUGIN_PATH || "../../docusaurus-plugin-moonwave"
419
+ }`
420
+ )
421
+ const packageJsonPath = path.join(tempDir, "package.json")
422
+
423
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"))
424
+ packageJson.dependencies["docusaurus-plugin-moonwave"] =
425
+ moonwavePluginPath
426
+ ? path.resolve(process.cwd(), moonwavePluginPath)
427
+ : path.resolve(__dirname, "../../docusaurus-plugin-moonwave")
428
+
429
+ fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2))
430
+ }
431
+ }
432
+
433
+ // Create home page or copy readme
434
+ makeHomePage(projectDir, tempDir, config)
435
+ // Copy CHANGELOG.md if it exists
436
+ const changelogExists = copyChangelog(projectDir, tempDir, config)
437
+
438
+ const foundFolders = copyContentFolders(projectDir, tempDir)
439
+
440
+ const { customCssExists, customSidebarExists } = copyMoonwaveFolder(
441
+ projectDir,
442
+ tempDir
443
+ )
444
+
445
+ const docusaurusConfigModified = writeDocusaurusConfig(tempDir, {
446
+ config,
447
+ enablePlugins: foundFolders,
448
+ customCssExists,
449
+ customSidebarExists,
450
+ codePaths: options.codePaths,
451
+ binaryPath: options.binaryPath,
452
+ changelogExists,
453
+ projectDir,
454
+ classOrder: config.classOrder ?? [],
455
+ apiCategories: config.apiCategories ?? [],
456
+ autoSectionPath: config.autoSectionPath,
457
+ })
458
+
459
+ if (
460
+ !fs.existsSync(path.join(tempDir, "./node_modules")) ||
461
+ !fs.existsSync(path.join(tempDir, "./node_modules/.bin/docusaurus"))
462
+ ) {
463
+ console.log("Installing dependencies (this might take awhile)...")
464
+
465
+ execSync("npm i", {
466
+ cwd: tempDir,
467
+ stdio: "inherit",
468
+ })
469
+ }
470
+
471
+ return {
472
+ docusaurusConfigModified,
473
+ tempDir,
474
+ projectDir,
475
+ watchPaths: [
476
+ path.join(
477
+ projectDir,
478
+ typeof config.home?.includeReadme === "string"
479
+ ? config.home.includeReadme
480
+ : "README.md"
481
+ ),
482
+ path.join(projectDir, "moonwave.toml"),
483
+ path.join(projectDir, "moonwave.json"),
484
+ path.join(projectDir, "CHANGELOG.md"),
485
+ path.join(projectDir, ".moonwave/"),
486
+ ...Object.entries(foundFolders)
487
+ // .filter(([_folder, wasFound]) => wasFound)
488
+ .map(([folder]) => folder)
489
+ .map((folder) => path.join(projectDir, folder)),
490
+ ],
491
+ }
492
+ }
@@ -0,0 +1,5 @@
1
+ declare module "cachedir" {
2
+ function cachedir(name: string): string
3
+
4
+ export = cachedir
5
+ }
@@ -0,0 +1,84 @@
1
+ import Link from "@docusaurus/Link"
2
+ import useDocusaurusContext from "@docusaurus/useDocusaurusContext"
3
+ import Layout from "@theme/Layout"
4
+ import clsx from "clsx"
5
+ import React from "react"
6
+ import styles from "./index.module.css"
7
+
8
+ const FEATURES = /***features***/
9
+
10
+ function Feature({ image, title, description }) {
11
+ return (
12
+ <div className={clsx("col col--4")}>
13
+ {image && (
14
+ <div className="text--center">
15
+ <img className={styles.featureSvg} alt={title} src={image} />
16
+ </div>
17
+ )}
18
+ <div className="text--center padding-horiz--md">
19
+ <h3>{title}</h3>
20
+ <p>{description}</p>
21
+ </div>
22
+ </div>
23
+ )
24
+ }
25
+
26
+ export function HomepageFeatures() {
27
+ if (!FEATURES) return null
28
+
29
+ return (
30
+ <section className={styles.features}>
31
+ <div className="container">
32
+ <div className="row">
33
+ {FEATURES.map((props, idx) => (
34
+ <Feature key={idx} {...props} />
35
+ ))}
36
+ </div>
37
+ </div>
38
+ </section>
39
+ )
40
+ }
41
+
42
+ function HomepageHeader() {
43
+ const { siteConfig } = useDocusaurusContext()
44
+ const bannerImage = siteConfig.customFields.bannerImage
45
+ const hasBannerImage = bannerImage ? true : false
46
+ const heroBannerStyle = hasBannerImage ? { backgroundImage: `url("${bannerImage}")` } : null
47
+
48
+ const titleClassName = clsx("hero__title", {
49
+ [styles.titleOnBannerImage]: hasBannerImage
50
+ })
51
+ const taglineClassName = clsx("hero__subtitle", {
52
+ [styles.taglineOnBannerImage]: hasBannerImage
53
+ })
54
+
55
+ return (
56
+ <header className={clsx("hero", styles.heroBanner)} style={heroBannerStyle}>
57
+ <div className="container">
58
+ <h1 className={titleClassName}>{siteConfig.title}</h1>
59
+ <p className={taglineClassName}>{siteConfig.tagline}</p>
60
+ <div className={styles.buttons}>
61
+ <Link
62
+ className="button button--secondary button--lg"
63
+ to="/docs/intro"
64
+ >
65
+ Get Started →
66
+ </Link>
67
+ </div>
68
+ </div>
69
+ </header>
70
+ )
71
+ }
72
+
73
+ export default function Home() {
74
+ const { siteConfig, tagline } = useDocusaurusContext()
75
+ return (
76
+ <Layout title={siteConfig.title} description={tagline}>
77
+ <HomepageHeader />
78
+ <main>
79
+ <HomepageFeatures />
80
+ <div className="container">{/***readme***/}</div>
81
+ </main>
82
+ </Layout>
83
+ )
84
+ }
@@ -0,0 +1,51 @@
1
+ /* stylelint-disable docusaurus/copyright-header */
2
+
3
+ /**
4
+ * CSS files with the .module.css suffix will be treated as CSS modules
5
+ * and scoped locally.
6
+ */
7
+
8
+ .heroBanner {
9
+ padding: 4rem 0;
10
+ text-align: center;
11
+ position: relative;
12
+ overflow: hidden;
13
+
14
+ background-repeat: no-repeat;
15
+ background-size: cover;
16
+ background-position: center;
17
+ }
18
+
19
+ @media screen and (max-width: 966px) {
20
+ .heroBanner {
21
+ padding: 2rem;
22
+ }
23
+ }
24
+
25
+ .buttons {
26
+ display: flex;
27
+ align-items: center;
28
+ justify-content: center;
29
+ }
30
+
31
+ .features {
32
+ display: flex;
33
+ align-items: center;
34
+ padding: 2rem 0;
35
+ width: 100%;
36
+ }
37
+
38
+ .featureSvg {
39
+ height: 200px;
40
+ width: 200px;
41
+ }
42
+
43
+ .titleOnBannerImage {
44
+ text-shadow: 0px 2px 4px rgb(0, 0, 0);
45
+ color: var(--ifm-color-gray-100);
46
+ }
47
+
48
+ .taglineOnBannerImage {
49
+ text-shadow: 0px 2px 4px rgb(0, 0, 0);
50
+ color: var(--ifm-color-gray-100);
51
+ }
@@ -0,0 +1,3 @@
1
+ module.exports = {
2
+ presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
3
+ };