A custom TypeScript/JSX build tool for creating components compatible with Datacore's custom Preact runtime. Datacore is an Obsidian plugin that allows you to query your notes and render data as Preact components.
Papacore transforms TypeScript and JSX files into a format consumable by Datacore's custom Preact runtime. The build system:
- Transforms
.tsxfiles →.jsxfiles (preserving JSX syntax) - Transforms
.tsfiles →.jsfiles - Converts ES6 imports/exports to
dc.require()calls - Prefixes React hooks with
dc.(e.g.,useState→dc.useState) - Resolves all imports to absolute paths from the
dist/root
# Install dependencies
pnpm installBefore you can deploy your components to your Datacore vault, you need to configure the target location:
pnpm run configThis will prompt you to enter the path to your Datacore vault. The configuration is saved in papacore.json (which is git-ignored for security).
You can run pnpm run config again anytime to update the vault location.
Compile all source files once:
pnpm run buildContinuously watch for changes and rebuild automatically:
pnpm run watchBuild, watch for changes, and automatically install to your configured vault:
pnpm run devThis will:
- Ask for confirmation once at startup
- Build all files
- Copy them to your vault
- Watch for changes and auto-install on every save
Copy already-built files from dist/ to your configured vault:
pnpm run installThis will ask for confirmation before overwriting files.
papacore/
├── src/ # Source files (.ts, .tsx)
│ ├── index.tsx
│ ├── counter.tsx
│ └── Datacore/
│ └── utils/
│ └── time.ts
├── dist/ # Compiled output (.js, .jsx)
│ ├── index.jsx
│ ├── counter.jsx
│ └── Datacore/
│ └── utils/
│ └── time.js
├── scripts/ # Build and utility scripts
│ ├── build.js # Build script with watch mode
│ ├── install.js # Install to vault script
│ ├── config.js # Configuration wizard
│ └── utils.js # Shared utilities
├── babel-plugins/ # Custom Babel transformation plugins
│ ├── transform-imports-exports.js
│ └── transform-react-hooks.js
├── babel.config.js # Babel configuration
└── papacore.json # Vault configuration (git-ignored)
src/index.tsx:
import { Counter } from "./counter";
import { getTodayString } from "./Datacore/utils/time";
export function App() {
return (
<div>
<p>Today is {getTodayString()}</p>
<Counter />
</div>
);
}dist/index.jsx:
const { Counter } = await dc.require("counter.jsx");
const { getTodayString } = await dc.require("Datacore/utils/time.js");
function App() {
return <div>
<p>Today is {getTodayString()}</p>
<Counter />
</div>;
}
return { App };-
Relative imports →
dc.require()with absolute paths fromdist/root// Source import { foo } from "./bar"; // Output const { foo } = await dc.require("bar.jsx");
-
React imports → Removed (React is available on
dcglobal)// Source import { useState } from "react"; // Output // (removed)
-
Type-only imports → Removed
// Source import type { MyType } from "./types"; // Output // (removed)
-
Exports → Returned as object at end of file
// Source export function Component() { ... } export const value = 42; // Output function Component() { ... } const value = 42; return { Component, value };
All React APIs are prefixed with dc.:
// Source
const [count, setCount] = useState(0);
useEffect(() => { ... }, [count]);
// Output
const [count, setCount] = dc.useState(0);
dc.useEffect(() => { ... }, [count]);JSX syntax is preserved in the output (not converted to React.createElement):
// Source & Output (same)
return <div className="container">
<Button onClick={handleClick}>Click me</Button>
</div>;Import paths automatically map source extensions to output extensions:
| Source Import | Source File | Output Import |
|---|---|---|
"./component" |
component.tsx |
"component.jsx" |
"./utils" |
utils.ts |
"utils.js" |
The build system uses two custom Babel plugins:
- transform-imports-exports.js - Handles import/export transformation and path resolution
- transform-react-hooks.js - Prefixes React identifiers with
dc.
- Node.js (for running the build script)
- pnpm (package manager)
- Install dependencies:
pnpm install - Configure your vault:
pnpm run config - Start development mode:
pnpm run dev - Edit files in
src/and they'll automatically compile and install to your vault
Option 1: Auto-install to vault (recommended)
pnpm run devThis watches for changes, rebuilds, and automatically copies files to your vault.
Option 2: Build only
pnpm run watchThis watches for changes and rebuilds, but doesn't install to your vault.
Option 3: Manual build + install
pnpm run build
pnpm run installBuild once and manually copy to your vault.
| Command | Description |
|---|---|
pnpm run config |
Configure vault location (interactive wizard) |
pnpm run build |
Build once (compile all files) |
pnpm run watch |
Watch mode (rebuild on changes) |
pnpm run dev |
Development mode (build + watch + auto-install to vault) |
pnpm run install |
Copy built files to vault (with confirmation) |
MIT License - see LICENSE file for details.
This project is free to use, modify, and distribute with attribution.