Skip to content

Commit 7c3c600

Browse files
authored
[orga-build] fix react resolve issue (#256)
* [orga-build] fix resolve react/jsx-runtime issue * remove packageManager * ignore .orga-build folder * [orga-build] tidy up * add option to custom href in oast-to-hast * add orga-build example * add changeset * fix tests
1 parent c2b850e commit 7c3c600

24 files changed

+172
-153
lines changed

.changeset/many-monkeys-retire.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'oast-to-hast': patch
3+
'orga-build': patch
4+
---
5+
6+
fix react resolve issue

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -105,3 +105,6 @@ dist
105105
*.d.ts.map
106106
!packages/reorg-parse/index.d.ts
107107
!packages/loader/index.d.ts
108+
109+
.meta.json
110+
.orga-build

examples/build/index.org

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#+title: Build with Orga
2+
3+
* Hi
4+
5+
You can build a static website with *just* org files.
6+
7+
Here's [[file:more.org][another page]].
8+
9+
#+begin_src sh
10+
orga-build
11+
#+end_src

examples/build/more.org

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#+title: Another Page
2+
3+
This is another page.

examples/build/package.json

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"name": "@orgajs/example-build",
3+
"private": true,
4+
"type": "module",
5+
"scripts": {
6+
"dev": "orga-build dev",
7+
"build": "orga-build"
8+
},
9+
"devDependencies": {
10+
"orga-build": "workspace:^"
11+
}
12+
}

package.json

-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
"ci:version": "changeset version",
2121
"ci:publish": "pnpm build && changeset publish"
2222
},
23-
"packageManager": "pnpm@10",
2423
"xo": {
2524
"prettier": true
2625
}

packages/oast-to-hast/index.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* @typedef {import('./lib/state.js').Options} Options
2+
* @typedef {import('./lib/index.js').Options} Options
33
*/
44

55
export { handlers as defaultHandlers } from './lib/handlers/index.js'

packages/oast-to-hast/lib/handlers/link.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ export function link(state, node) {
6161
type: 'element',
6262
tagName: 'a',
6363
properties: {
64-
href: node.path.value,
64+
href: state.options.linkHref(node),
6565
target: state.options.linkTarget
6666
},
6767
children: state.all(node)

packages/oast-to-hast/lib/handlers/section.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export function section(state, node) {
3333
}
3434

3535
/**
36-
* @param {import('../state.js').Options} options
36+
* @param {import('../state.js').Config} config
3737
* @param {string[]} tags
3838
* @returns {boolean}
3939
*/

packages/oast-to-hast/lib/index.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
/**
22
* @typedef {import('hast').Nodes} HastNodes
33
* @typedef {import('orga').Nodes} OastNodes
4+
* @typedef {Partial<import('./state.js').Config> | null | undefined} Options
45
*/
56

67
import { createState } from './state.js'
78

89
/**
910
* @param {OastNodes} tree
1011
* oast tree.
11-
* @param {import('./state.js').Options | null | undefined} [options]
12+
* @param {Options} [options]
1213
* Configuration (optional).
1314
* @returns {HastNodes}
1415
* hast tree.

packages/oast-to-hast/lib/state.js

+14-7
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@
66
*/
77

88
/**
9-
* @typedef {Object} Options
10-
* @property {Handlers} [handlers]
11-
* @property {string} [linkTarget='_self']
12-
* @property {string[]} [selectTags=[]]
13-
* @property {string[]} [excludeTags=['noexport']]
9+
* @typedef {Object} Config
10+
* @property {Handlers} handlers
11+
* @property {string} linkTarget
12+
* @property {(link: import('orga').Link) => string} linkHref
13+
* @property {string[]} selectTags=[]
14+
* @property {string[]} excludeTags=['noexport']
1415
*/
1516

1617
/**
@@ -32,22 +33,28 @@ import { handlers as defaultHandlers } from './handlers/index.js'
3233

3334
/**
3435
* @param {OastNodes} tree
35-
* @param {Options | null | undefined} [options = {}]
36+
* @param {Partial<Config> | null | undefined} [options = {}]
3637
*/
3738
export function createState(tree, options = {}) {
3839
/** @type {Handlers} */
39-
const handlers = { ...defaultHandlers, ...options?.handlers }
40+
let handlers = { ...defaultHandlers }
41+
if (options?.handlers) {
42+
handlers = { ...handlers, ...options.handlers }
43+
}
4044

4145
const state = {
4246
one,
4347
all,
4448
handlers,
4549
getAttrHtml,
4650
patch,
51+
/** @type {Config} */
4752
options: {
53+
handlers,
4854
linkTarget: '_self',
4955
selectTags: [],
5056
excludeTags: ['noexport'],
57+
linkHref: (link) => link.path.value,
5158
...options
5259
}
5360
}

packages/orga-build/cli.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
#!/usr/bin/env node
22

3-
import { argv } from 'node:process'
3+
import { argv, cwd } from 'node:process'
44
import { parseArgs } from 'node:util'
55
import { watch } from './lib/watch.js'
6-
import { loadConfig, clean } from './lib/build.js'
7-
import { build } from './lib/esbuild/index.js'
6+
import { build, clean } from './lib/esbuild/build.js'
7+
import { loadConfig } from './lib/config.js'
88
import { serve } from './lib/serve.js'
99

1010
const { values, positionals } = parseArgs({
@@ -17,7 +17,7 @@ const { values, positionals } = parseArgs({
1717
allowPositionals: true
1818
})
1919

20-
const config = await loadConfig()
20+
const config = await loadConfig(cwd(), 'orga.config.js', 'orga.config.ts')
2121

2222
await build(config)
2323

packages/orga-build/index.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
export { build } from './lib/build.js'
1+
export { build } from './lib/esbuild/build.js'

packages/orga-build/lib/config.js

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import fs from 'node:fs/promises'
2+
import path from 'node:path'
3+
import { evaluate } from './esbuild/evaluate.js'
4+
5+
/**
6+
* @typedef {Object} Options
7+
* @property {string} [outDir]
8+
* @property {string[]} [preBuild]
9+
* @property {string[]} [postBuild]
10+
*/
11+
12+
/** @type {Options} */
13+
const defaultConfig = {
14+
outDir: 'out',
15+
preBuild: [],
16+
postBuild: []
17+
}
18+
19+
/**
20+
* @param {string} cwd
21+
* @param {string[]} files
22+
* @returns {Promise<Options>}
23+
*/
24+
export async function loadConfig(cwd, ...files) {
25+
for (const file of files) {
26+
const filePath = path.join(cwd, file)
27+
try {
28+
await fs.access(filePath, fs.constants.F_OK)
29+
const config = await evaluate(filePath)
30+
return { ...defaultConfig, ...config }
31+
} catch (err) {}
32+
}
33+
return defaultConfig
34+
}

packages/orga-build/lib/esbuild/index.js packages/orga-build/lib/esbuild/build.js

+26-9
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,10 @@ import { renderToString } from 'react-dom/server'
77
import assert from 'node:assert'
88
import { DefaultLayout, $, match } from '../util.js'
99
import rawLoader from './raw-loader.js'
10+
import resolveReact from './resolve-react.js'
1011

1112
/**
12-
* @typedef {Object} Options
13-
* @property {string} [outDir]
14-
* @property {string[]} [preBuild]
15-
* @property {string[]} [postBuild]
16-
*/
17-
18-
/**
19-
* @param {Options} options
13+
* @param {import('../config.js').Options} options
2014
*/
2115
export async function build({ outDir = 'dir', preBuild = [], postBuild = [] }) {
2216
for (const cmd of preBuild) {
@@ -62,12 +56,28 @@ export async function build({ outDir = 'dir', preBuild = [], postBuild = [] }) {
6256
format: 'esm',
6357
platform: 'node',
6458
target: 'esnext',
59+
// jsxFactory: 'React.createElement',
60+
// jsxFragment: 'React.Fragment',
6561
jsx: 'automatic',
6662
// write: false,
6763
outdir: '.orga-build/js',
6864
// splitting: true,
6965
metafile: true,
70-
plugins: [esbuildOrga(), rawLoader],
66+
plugins: [
67+
esbuildOrga({
68+
reorgRehypeOptions: {
69+
linkHref: (link) => {
70+
if (link.path.protocol === 'file') {
71+
return link.path.value.replace(/\.org$/, '.html')
72+
}
73+
return link.path.value
74+
}
75+
}
76+
// reorgPlugins: [reorgLinks]
77+
}),
78+
rawLoader,
79+
resolveReact
80+
],
7181
// external: ['react/jsx-runtime'],
7282
loader: {
7383
'.jsx': 'jsx',
@@ -215,3 +225,10 @@ async function walk(dirPath, callback) {
215225
}
216226
}
217227
}
228+
229+
/**
230+
* @param {import("fs").PathLike} dir
231+
*/
232+
export async function clean(dir) {
233+
await fs.rm(dir, { recursive: true })
234+
}

packages/orga-build/lib/esbuild.js packages/orga-build/lib/esbuild/evaluate.js

-24
Original file line numberDiff line numberDiff line change
@@ -31,27 +31,3 @@ export async function evaluate(filePath) {
3131
`return import("data:application/javascript,${encodeURIComponent(code)}")`
3232
)()
3333
}
34-
35-
/**
36-
* @param {string} pattern
37-
*/
38-
export async function build(pattern) {
39-
return await esbuild.build({
40-
entryPoints: [pattern],
41-
entryNames: '[dir]/_/[name]',
42-
bundle: true,
43-
format: 'esm',
44-
platform: 'node',
45-
target: 'esnext',
46-
jsx: 'automatic',
47-
// write: false,
48-
outdir: '.build',
49-
plugins: [esbuildOrga()],
50-
loader: {
51-
'.jsx': 'jsx',
52-
'.tsx': 'tsx'
53-
}
54-
})
55-
}
56-
57-
export async function b() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/**
2+
* @import {PluginBuild} from 'esbuild'
3+
*/
4+
5+
import { createRequire } from 'module'
6+
import path from 'path'
7+
8+
const require = createRequire(import.meta.url)
9+
const reactPath = require.resolve('react')
10+
const reactDirPath = path.dirname(reactPath)
11+
12+
const resolveReact = {
13+
name: 'resolve-react',
14+
/**
15+
* @param {PluginBuild} build
16+
* Build.
17+
* @returns {undefined}
18+
* Nothing.
19+
*/
20+
setup(build) {
21+
build.onResolve({ filter: /^react\/jsx-runtime$/ }, () => {
22+
return { path: path.join(reactDirPath, 'jsx-runtime.js') }
23+
})
24+
}
25+
}
26+
27+
export default resolveReact

packages/orga-build/lib/build.js packages/orga-build/lib/node/build.js

+5-15
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,11 @@ import path from 'path'
55
import { createElement } from 'react'
66
import { renderToString } from 'react-dom/server'
77
import assert from 'node:assert'
8-
import { match } from './util.js'
9-
import { evaluate, build as _build } from './esbuild.js'
10-
import { $, DefaultLayout } from './util.js'
8+
import { match, $, DefaultLayout } from '../util.js'
119

12-
const USE_NODE = false
13-
14-
if (USE_NODE) {
15-
register('./jsx-loader.js', import.meta.url)
16-
register('./orga-loader.js', import.meta.url)
17-
register('./raw-loader.js', import.meta.url)
18-
}
10+
register('./jsx-loader.js', import.meta.url)
11+
register('./orga-loader.js', import.meta.url)
12+
register('./raw-loader.js', import.meta.url)
1913

2014
const defaultConfig = {
2115
outDir: 'out',
@@ -208,11 +202,7 @@ async function _import(...files) {
208202
const file = found[0]
209203
const fullPath = path.isAbsolute(file) ? file : path.join(process.cwd(), file)
210204
const { mtime } = await fs.stat(fullPath)
211-
if (USE_NODE) {
212-
return await import(`${fullPath}?version=${mtime.getTime()}`)
213-
} else {
214-
return await evaluate(fullPath)
215-
}
205+
return await import(`${fullPath}?version=${mtime.getTime()}`)
216206
}
217207

218208
/**

packages/orga-build/lib/watch.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export async function watch(dir, ignore, onChange) {
1111
/** @type {ReturnType<typeof setTimeout> | null} */
1212
let timeout = null
1313
const delay = 1000
14-
const defaultIgnorePattern = /node_modules|\.git|\.DS_Store/
14+
const defaultIgnorePattern = /node_modules|\.git|\.DS_Store|\.orga-build/
1515

1616
const watcher = fs.watch(dir, { recursive: true })
1717
for await (const event of watcher) {

packages/orga-build/package.json

+4-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"description": "A simple tool that builds org-mode files into a website",
55
"type": "module",
66
"bin": {
7-
"orga-build": "cli.js"
7+
"orga-build": "./cli.js"
88
},
99
"scripts": {},
1010
"files": ["lib/", "cli.js", "index.js", "index.d.ts", "index.d.ts.map"],
@@ -13,7 +13,7 @@
1313
"author": "Xiaoxing Hu <[email protected]>",
1414
"license": "MIT",
1515
"dependencies": {
16-
"@orgajs/esbuild": "^1.1.1",
16+
"@orgajs/esbuild": "workspace:^",
1717
"@orgajs/node-loader": "workspace:^",
1818
"esbuild": "^0.24.2",
1919
"globby": "^14.0.2",
@@ -27,6 +27,7 @@
2727
"@types/node": "^22.13.1",
2828
"@types/react": "^19.0.8",
2929
"@types/react-dom": "^19.0.3",
30-
"@types/serve-handler": "^6.1.4"
30+
"@types/serve-handler": "^6.1.4",
31+
"orga": "workspace:^"
3132
}
3233
}

0 commit comments

Comments
 (0)