Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
7 changes: 4 additions & 3 deletions cppjs-core/cpp.js/src/integration/getCppJsScript.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,10 @@ function getWebScript(env, modulePrefix) {
...config,
env: {...${env}, ...config.env},
paths: {
wasm: 'cpp.wasm',
data: 'cpp.data.txt',
worker: 'cpp.js'
wasm: '/cpp.wasm',
data: '/cpp.data.txt',
worker: '/cpp.js',
...config.paths
}
}`;

Expand Down
14 changes: 13 additions & 1 deletion cppjs-core/cppjs-core-create-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,29 @@
"dependencies": {
"cpp.js": "workspace:^",
"@cpp.js/sample-lib-cmake": "workspace:^",
"@cpp.js/sample-lib-cmake-multithread": "workspace:^",
"@cpp.js/sample-lib-prebuilt-matrix": "workspace:^",
"@cpp.js/sample-lib-prebuilt-matrix-multithread": "workspace:^",
"@cpp.js/sample-lib-source": "workspace:^",
"@cpp.js/sample-lib-source-multithread": "workspace:^",
"@cpp.js/sample-web-vanilla": "workspace:^",
"@cpp.js/sample-web-react-rspack": "workspace:^",
"@cpp.js/sample-web-react-vite": "workspace:^",
"@cpp.js/sample-web-react-vite-multithread": "workspace:^",
"@cpp.js/sample-web-svelte-vite": "workspace:^",
"@cpp.js/sample-web-svelte-vite-multithread": "workspace:^",
"@cpp.js/sample-web-vue-vite": "workspace:^",
"@cpp.js/sample-web-next-webpack": "workspace:^",
"@cpp.js/sample-web-next-webpack-multithread": "workspace:^",
"@cpp.js/sample-web-nuxt-vite": "workspace:^",
"@cpp.js/sample-web-nuxt-vite-multithread": "workspace:^",
"@cpp.js/sample-backend-nodejs-wasm": "workspace:^",
"@cpp.js/sample-backend-nodejs-wasm-multithread": "workspace:^",
"@cpp.js/sample-cloud-cloudflare-worker": "workspace:^",
"@cpp.js/sample-mobile-reactnative-cli": "workspace:^",
"@cpp.js/sample-mobile-reactnative-cli-multithread": "workspace:^",
"@cpp.js/sample-mobile-reactnative-expo": "1.0.1",
"@cpp.js/sample-mobile-reactnative-expo-multithread": "workspace:^",
"fs-extra": "^11.2.0",
"kleur": "^4.1.5",
"prompts": "^2.4.2",
Expand All @@ -45,4 +57,4 @@
"android",
"ios"
]
}
}
27 changes: 24 additions & 3 deletions cppjs-core/cppjs-core-create-app/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,13 +121,34 @@ async function main() {
}));
}

let templatePath;
// Get the template object
let template;
if (selectedBundler) {
templatePath = samples[selectedType][selectedPlatform][selectedBundler].path;
template = samples[selectedType][selectedPlatform][selectedBundler];
} else {
templatePath = samples[selectedType][selectedPlatform].path;
template = samples[selectedType][selectedPlatform];
}

// Check if multithread is available and ask user
let useMultithread = false;
if (template.multithreadPath) {
({ useMultithread } = await prompts({
type: 'confirm',
name: 'useMultithread',
message: 'Enable multithread support?',
initial: false,
}, {
onCancel: () => {
process.exit(1);
},
}));
}

// Determine the template path based on multithread selection
const templatePath = useMultithread && template.multithreadPath
? template.multithreadPath
: template.path;

fse.copySync(templatePath, cwd, { overwrite: true });

const packageJsonFilePath = `${cwd}/package.json`;
Expand Down
32 changes: 32 additions & 0 deletions cppjs-core/cppjs-core-create-app/src/samples.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,31 @@
import getParentPath from 'cpp.js/src/utils/getParentPath.js';

import sampleLibCmake from '@cpp.js/sample-lib-cmake/cppjs.config.js';
import sampleLibCmakeMultithread from '@cpp.js/sample-lib-cmake-multithread/cppjs.config.js';
import sampleLibMatrix from '@cpp.js/sample-lib-prebuilt-matrix/cppjs.config.js';
import sampleLibMatrixMultithread from '@cpp.js/sample-lib-prebuilt-matrix-multithread/cppjs.config.js';
import sampleLibSource from '@cpp.js/sample-lib-source/cppjs.config.js';
import sampleLibSourceMultithread from '@cpp.js/sample-lib-source-multithread/cppjs.config.js';

import sampleMobileReactNativeCLI from '@cpp.js/sample-mobile-reactnative-cli/cppjs.config.mjs';
import sampleMobileReactNativeCLIMultithread from '@cpp.js/sample-mobile-reactnative-cli-multithread/cppjs.config.mjs';
import sampleMobileReactNativeExpo from '@cpp.js/sample-mobile-reactnative-expo/cppjs.config.mjs';
import sampleMobileReactNativeExpoMultithread from '@cpp.js/sample-mobile-reactnative-expo-multithread/cppjs.config.mjs';

import sampleWebVanilla from '@cpp.js/sample-web-vanilla/cppjs.config.mjs';
import sampleWebReactRspack from '@cpp.js/sample-web-react-rspack/cppjs.config.mjs';
import sampleWebReactVite from '@cpp.js/sample-web-react-vite/cppjs.config.js';
import sampleWebReactViteMultithread from '@cpp.js/sample-web-react-vite-multithread/cppjs.config.js';
import sampleWebSvelteVite from '@cpp.js/sample-web-svelte-vite/cppjs.config.js';
import sampleWebSvelteViteMultithread from '@cpp.js/sample-web-svelte-vite-multithread/cppjs.config.js';
import sampleWebVueVite from '@cpp.js/sample-web-vue-vite/cppjs.config.js';
import sampleWebNextWebpack from '@cpp.js/sample-web-next-webpack/cppjs.config.js';
import sampleWebNextWebpackMultithread from '@cpp.js/sample-web-next-webpack-multithread/cppjs.config.js';
import sampleWebNuxtVite from '@cpp.js/sample-web-nuxt-vite/cppjs.config.js';
import sampleWebNuxtViteMultithread from '@cpp.js/sample-web-nuxt-vite-multithread/cppjs.config.js';

import sampleBackendNodeJsWasm from '@cpp.js/sample-backend-nodejs-wasm/cppjs.config.mjs';
import sampleBackendNodeJsWasmMultithread from '@cpp.js/sample-backend-nodejs-wasm-multithread/cppjs.config.mjs';
import sampleCloudCloudflareWorker from '@cpp.js/sample-cloud-cloudflare-worker/cppjs.config.mjs';

export default {
Expand All @@ -31,6 +43,7 @@ export default {
},
Vite: {
path: getParentPath(sampleWebReactVite.paths.config),
multithreadPath: getParentPath(sampleWebReactViteMultithread.paths.config),
},
},
Vue: {
Expand All @@ -41,6 +54,19 @@ export default {
Svelte: {
Vite: {
path: getParentPath(sampleWebSvelteVite.paths.config),
multithreadPath: getParentPath(sampleWebSvelteViteMultithread.paths.config),
},
},
'Next.js': {
Webpack: {
path: getParentPath(sampleWebNextWebpack.paths.config),
multithreadPath: getParentPath(sampleWebNextWebpackMultithread.paths.config),
},
},
Nuxt: {
Vite: {
path: getParentPath(sampleWebNuxtVite.paths.config),
multithreadPath: getParentPath(sampleWebNuxtViteMultithread.paths.config),
},
},
},
Expand All @@ -52,9 +78,11 @@ export default {
'React Native': {
'Native CLI': {
path: getParentPath(sampleMobileReactNativeCLI.paths.config),
multithreadPath: getParentPath(sampleMobileReactNativeCLIMultithread.paths.config),
},
Expo: {
path: getParentPath(sampleMobileReactNativeExpo.paths.config),
multithreadPath: getParentPath(sampleMobileReactNativeExpoMultithread.paths.config),
},
},
},
Expand All @@ -66,6 +94,7 @@ export default {
'Node.js': {
WebAssembly: {
path: getParentPath(sampleBackendNodeJsWasm.paths.config),
multithreadPath: getParentPath(sampleBackendNodeJsWasmMultithread.paths.config),
},
},
},
Expand All @@ -83,12 +112,15 @@ export default {
],
Prebuilt: {
path: getParentPath(sampleLibMatrix.paths.config),
multithreadPath: getParentPath(sampleLibMatrixMultithread.paths.config),
},
Source: {
path: getParentPath(sampleLibSource.paths.config),
multithreadPath: getParentPath(sampleLibSourceMultithread.paths.config),
},
CMake: {
path: getParentPath(sampleLibCmake.paths.config),
multithreadPath: getParentPath(sampleLibCmakeMultithread.paths.config),
},
},
};
85 changes: 72 additions & 13 deletions cppjs-plugins/cppjs-plugin-vite/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,27 @@ import rollupCppjsPlugin from '@cpp.js/plugin-rollup';

import fs from 'node:fs';

let wasmBuilt = false;

const viteCppjsPlugin = (options) => {
let isServe = false;
const bridges = [];
const headerRegex = new RegExp(`\\.(${state.config.ext.header.join('|')})$`);
const sourceRegex = new RegExp(`\\.(${state.config.ext.source.join('|')})$`);

async function ensureWasmBuilt() {
if (!wasmBuilt) {
createLib('Emscripten-x86_64', 'Source', { isProd: false, buildSource: true });
createLib('Emscripten-x86_64', 'Bridge', { isProd: false, buildSource: false, nativeGlob: [`${state.config.paths.cli}/assets/commonBridges.cpp`, ...bridges] });
await buildWasm('browser', false);
wasmBuilt = true;
}
}

return [
rollupCppjsPlugin(options, bridges),
{
name: 'vite-plugin-cppjs',
async load(source) {
if (isServe && source === '/cpp.js') {
createLib('Emscripten-x86_64', 'Source', { isProd: false, buildSource: true });
createLib('Emscripten-x86_64', 'Bridge', { isProd: false, buildSource: false, nativeGlob: [`${state.config.paths.cli}/assets/commonBridges.cpp`, ...bridges] });
await buildWasm('browser', false);
return fs.readFileSync(`${state.config.paths.build}/${state.config.general.name}.browser.js`, { encoding: 'utf8', flag: 'r' });
}
return null;
},
configResolved(config) {
isServe = config.command === 'serve';
if (isServe) {
Expand All @@ -31,12 +33,69 @@ const viteCppjsPlugin = (options) => {
},
configureServer(server) {
if (isServe) {
server.middlewares.use((req, res, next) => {
// Serve cpp.js as ES module - must be before Vite's middleware
server.middlewares.use(async (req, res, next) => {
res.setHeader("Cross-Origin-Opener-Policy", "same-origin");
res.setHeader("Cross-Origin-Embedder-Policy", "require-corp");
if (req.url === '/cpp.wasm') req.url = `/@fs/${state.config.paths.build}/${state.config.general.name}.wasm`;
else if (req.url === '/cpp.data.txt') req.url = `/@fs/${state.config.paths.build}/${state.config.general.name}.data.txt`;
// else if (req.url === '/cpp.worker.js') req.url = `/@fs/${state.config.paths.build}/${state.config.general.name}.js`;

// Handle cpp.js - serve as ES module with global assignment
if (req.url === '/cpp.js' || req.url === '/_nuxt/cpp.js') {
try {
await ensureWasmBuilt();
const jsPath = `${state.config.paths.build}/${state.config.general.name}.browser.js`;
const content = fs.readFileSync(jsPath, 'utf-8');
res.setHeader('Content-Type', 'application/javascript');
res.end(content);
return;
} catch (err) {
console.error('Error serving cpp.js:', err);
}
}
// Handle wasm file - serve directly
else if (req.url === '/cpp.wasm' || req.url === '/_nuxt/cpp.wasm') {
try {
await ensureWasmBuilt();
const wasmPath = `${state.config.paths.build}/${state.config.general.name}.wasm`;
const wasmBuffer = fs.readFileSync(wasmPath);
res.setHeader('Content-Type', 'application/wasm');
res.setHeader('Content-Length', wasmBuffer.length.toString());
res.end(wasmBuffer);
return;
} catch (err) {
console.error('Error serving cpp.wasm:', err);
}
}
// Handle data file - serve directly
else if (req.url === '/cpp.data.txt' || req.url === '/_nuxt/cpp.data.txt') {
try {
const dataPath = `${state.config.paths.build}/${state.config.general.name}.data.txt`;
if (fs.existsSync(dataPath)) {
const dataContent = fs.readFileSync(dataPath);
res.setHeader('Content-Type', 'text/plain');
res.end(dataContent);
return;
}
} catch (err) {
console.error('Error serving cpp.data.txt:', err);
}
}
// Handle pthread worker file - serve the browser JS for web workers
// Workers request /cpp.browser.js in pthread mode
else if (req.url === '/cpp.browser.js' || req.url === '/_nuxt/cpp.browser.js' || req.url === '/cpp.worker.js' || req.url === '/_nuxt/cpp.worker.js') {
try {
await ensureWasmBuilt();
// Emscripten pthread mode uses the .browser.js file for workers
const workerJsPath = `${state.config.paths.build}/${state.config.general.name}.browser.js`;
if (fs.existsSync(workerJsPath)) {
const content = fs.readFileSync(workerJsPath, 'utf-8');
res.setHeader('Content-Type', 'application/javascript');
res.end(content);
return;
}
} catch (err) {
console.error('Error serving cpp.browser.js:', err);
}
}
next();
});
}
Expand Down
42 changes: 32 additions & 10 deletions cppjs-plugins/cppjs-plugin-webpack/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ import { state, createLib, buildWasm, createBridgeFile, getData, getCppJsScript

export default class CppjsWebpackPlugin {
static defaultOptions = {};
static hasBuilt = false; // Static flag shared across all instances (for Next.js multi-compiler)
static bridges = []; // Static bridges array shared across all instances

constructor(options = {}) {
this.options = { ...CppjsWebpackPlugin.defaultOptions, ...options };
this.bridges = [];
}

apply(compiler) {
Expand All @@ -23,27 +24,48 @@ export default class CppjsWebpackPlugin {

async onDone({ compilation }) {
const isDev = compilation.options.mode === 'development';

// In dev mode, skip rebuild if already built (prevents infinite loop)
// Use static flag to share state across multiple webpack instances (Next.js)
if (isDev && CppjsWebpackPlugin.hasBuilt) {
return;
}

// In dev mode, defer build until bridges is populated (loader has run)
// This handles the case where server compilation finishes before client compilation
if (isDev && CppjsWebpackPlugin.bridges.length === 0) {
return;
}

// Set flag BEFORE building to prevent race conditions with parallel onDone calls
CppjsWebpackPlugin.hasBuilt = true;



createLib('Emscripten-x86_64', 'Source', { isProd: true, buildSource: true });
createLib('Emscripten-x86_64', 'Bridge', { isProd: true, buildSource: false, nativeGlob: [`${state.config.paths.cli}/assets/commonBridges.cpp`, ...this.bridges] });
createLib('Emscripten-x86_64', 'Bridge', { isProd: true, buildSource: false, nativeGlob: [`${state.config.paths.cli}/assets/commonBridges.cpp`, ...CppjsWebpackPlugin.bridges] });
await buildWasm('browser', true);

if (!isDev) {
fs.copyFileSync(`${state.config.paths.build}/${state.config.general.name}.browser.js`, `${compilation.options.output.path}/cpp.js`);
fs.copyFileSync(`${state.config.paths.build}/${state.config.general.name}.wasm`, `${compilation.options.output.path}/cpp.wasm`);
const output = state.config.paths.output === state.config.paths.build ? compilation.options.output.path : state.config.paths.output;
fs.copyFileSync(`${state.config.paths.build}/${state.config.general.name}.browser.js`, `${output}/cpp.js`);
fs.copyFileSync(`${state.config.paths.build}/${state.config.general.name}.wasm`, `${output}/cpp.wasm`);

const dataFilePath = `${state.config.paths.build}/${state.config.general.name}.data.txt`;
if (fs.existsSync(dataFilePath)) {
fs.copyFileSync(dataFilePath, `${compilation.options.output.path}/cpp.data.txt`);
fs.copyFileSync(dataFilePath, `${output}/cpp.data.txt`);
}
// Copy browser.js for pthread worker support (workers load this file)
const browserJsPath = `${state.config.paths.build}/${state.config.general.name}.browser.js`;
if (fs.existsSync(browserJsPath)) {
fs.copyFileSync(browserJsPath, `${output}/cpp.browser.js`);
}
/* const workerFilePath = `${state.config.paths.build}/${state.config.general.name}.js`;
if (fs.existsSync(workerFilePath)) {
fs.copyFileSync(workerFilePath, `${compilation.options.output.path}/cpp.worker.js`);
} */
}
}

getLoaderOptions() {
return {
bridges: this.bridges,
bridges: CppjsWebpackPlugin.bridges,
createBridgeFile,
getData,
state,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
dist
.cppjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# @cpp.js/sample-backend-nodejs-wasm

## 1.0.1

### Patch Changes

- Updated dependencies
- cpp.js@1.0.4

## 1.0.0

### Major Changes

- 🚀 first stable release

### Patch Changes

- Updated dependencies
- @cpp.js/sample-lib-prebuilt-matrix@1.0.0

## 1.0.0-beta.14

### Patch Changes

- chore: add initial version of CHANGELOGS files
- Updated dependencies
- @cpp.js/sample-lib-prebuilt-matrix@1.0.0-beta.32
Loading