mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-19 09:33:53 +00:00
feat: release qwen-code CLI packages as a standalone bundled js, with necessary vendors
This commit is contained in:
@@ -17,24 +17,74 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { copyFileSync, existsSync, mkdirSync } from 'node:fs';
|
||||
import { copyFileSync, existsSync, mkdirSync, statSync } from 'node:fs';
|
||||
import { dirname, join, basename } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { glob } from 'glob';
|
||||
import fs from 'node:fs';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const root = join(__dirname, '..');
|
||||
const bundleDir = join(root, 'bundle');
|
||||
const distDir = join(root, 'dist');
|
||||
const coreVendorDir = join(root, 'packages', 'core', 'vendor');
|
||||
|
||||
// Create the bundle directory if it doesn't exist
|
||||
if (!existsSync(bundleDir)) {
|
||||
mkdirSync(bundleDir);
|
||||
// Create the dist directory if it doesn't exist
|
||||
if (!existsSync(distDir)) {
|
||||
mkdirSync(distDir);
|
||||
}
|
||||
|
||||
// Find and copy all .sb files from packages to the root of the bundle directory
|
||||
// Find and copy all .sb files from packages to the root of the dist directory
|
||||
const sbFiles = glob.sync('packages/**/*.sb', { cwd: root });
|
||||
for (const file of sbFiles) {
|
||||
copyFileSync(join(root, file), join(bundleDir, basename(file)));
|
||||
copyFileSync(join(root, file), join(distDir, basename(file)));
|
||||
}
|
||||
|
||||
console.log('Assets copied to bundle/');
|
||||
console.log('Copied sandbox profiles to dist/');
|
||||
|
||||
// Copy vendor directory (contains ripgrep binaries)
|
||||
console.log('Copying vendor directory...');
|
||||
if (existsSync(coreVendorDir)) {
|
||||
const destVendorDir = join(distDir, 'vendor');
|
||||
copyRecursiveSync(coreVendorDir, destVendorDir);
|
||||
console.log('Copied vendor directory to dist/');
|
||||
} else {
|
||||
console.warn(`Warning: Vendor directory not found at ${coreVendorDir}`);
|
||||
}
|
||||
|
||||
console.log('\n✅ All bundle assets copied to dist/');
|
||||
|
||||
/**
|
||||
* Recursively copy directory
|
||||
*/
|
||||
function copyRecursiveSync(src, dest) {
|
||||
if (!existsSync(src)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const stats = statSync(src);
|
||||
|
||||
if (stats.isDirectory()) {
|
||||
if (!existsSync(dest)) {
|
||||
mkdirSync(dest, { recursive: true });
|
||||
}
|
||||
|
||||
const entries = fs.readdirSync(src);
|
||||
for (const entry of entries) {
|
||||
// Skip .DS_Store files
|
||||
if (entry === '.DS_Store') {
|
||||
continue;
|
||||
}
|
||||
|
||||
const srcPath = join(src, entry);
|
||||
const destPath = join(dest, entry);
|
||||
copyRecursiveSync(srcPath, destPath);
|
||||
}
|
||||
} else {
|
||||
copyFileSync(src, dest);
|
||||
// Preserve execute permissions for binaries
|
||||
const srcStats = statSync(src);
|
||||
if (srcStats.mode & 0o111) {
|
||||
fs.chmodSync(dest, srcStats.mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
29
scripts/esbuild-shims.js
Normal file
29
scripts/esbuild-shims.js
Normal file
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Qwen
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Shims for esbuild ESM bundles to support require() calls
|
||||
* This file is injected into the bundle via esbuild's inject option
|
||||
*/
|
||||
|
||||
import { createRequire } from 'node:module';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { dirname } from 'node:path';
|
||||
|
||||
// Create require function for the current module and make it global
|
||||
const _require = createRequire(import.meta.url);
|
||||
|
||||
// Make require available globally for dynamic requires
|
||||
if (typeof globalThis.require === 'undefined') {
|
||||
globalThis.require = _require;
|
||||
}
|
||||
|
||||
// Export for esbuild injection
|
||||
export const require = _require;
|
||||
|
||||
// Setup __filename and __dirname for compatibility
|
||||
export const __filename = fileURLToPath(import.meta.url);
|
||||
export const __dirname = dirname(__filename);
|
||||
@@ -1,51 +1,110 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* Copyright 2025 Qwen
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Prepares the bundled CLI package for npm publishing
|
||||
* This script adds publishing metadata (package.json, README, LICENSE) to dist/
|
||||
* All runtime assets (cli.js, vendor/, *.sb) are already in dist/ from the bundle step
|
||||
*/
|
||||
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { execSync } from 'node:child_process';
|
||||
|
||||
// ES module equivalent of __dirname
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
const rootDir = path.resolve(__dirname, '..');
|
||||
|
||||
function copyFiles(packageName, filesToCopy) {
|
||||
const packageDir = path.resolve(rootDir, 'packages', packageName);
|
||||
if (!fs.existsSync(packageDir)) {
|
||||
console.error(`Error: Package directory not found at ${packageDir}`);
|
||||
process.exit(1);
|
||||
}
|
||||
const distDir = path.join(rootDir, 'dist');
|
||||
const cliBundlePath = path.join(distDir, 'cli.js');
|
||||
const vendorDir = path.join(distDir, 'vendor');
|
||||
|
||||
console.log(`Preparing package: ${packageName}`);
|
||||
for (const [source, dest] of Object.entries(filesToCopy)) {
|
||||
const sourcePath = path.resolve(rootDir, source);
|
||||
const destPath = path.resolve(packageDir, dest);
|
||||
try {
|
||||
fs.copyFileSync(sourcePath, destPath);
|
||||
console.log(`Copied ${source} to packages/${packageName}/`);
|
||||
} catch (err) {
|
||||
console.error(`Error copying ${source}:`, err);
|
||||
process.exit(1);
|
||||
}
|
||||
// Verify dist directory and bundle exist
|
||||
if (!fs.existsSync(distDir)) {
|
||||
console.error('Error: dist/ directory not found');
|
||||
console.error('Please run "npm run bundle" first');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!fs.existsSync(cliBundlePath)) {
|
||||
console.error(`Error: Bundle not found at ${cliBundlePath}`);
|
||||
console.error('Please run "npm run bundle" first');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!fs.existsSync(vendorDir)) {
|
||||
console.error(`Error: Vendor directory not found at ${vendorDir}`);
|
||||
console.error('Please run "npm run bundle" first');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Copy README and LICENSE
|
||||
console.log('Copying documentation files...');
|
||||
const filesToCopy = ['README.md', 'LICENSE'];
|
||||
for (const file of filesToCopy) {
|
||||
const sourcePath = path.join(rootDir, file);
|
||||
const destPath = path.join(distDir, file);
|
||||
if (fs.existsSync(sourcePath)) {
|
||||
fs.copyFileSync(sourcePath, destPath);
|
||||
console.log(`Copied ${file}`);
|
||||
} else {
|
||||
console.warn(`Warning: ${file} not found at ${sourcePath}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare 'core' package
|
||||
copyFiles('core', {
|
||||
'README.md': 'README.md',
|
||||
LICENSE: 'LICENSE',
|
||||
'.npmrc': '.npmrc',
|
||||
});
|
||||
// Copy package.json from root and modify it for publishing
|
||||
console.log('Creating package.json for distribution...');
|
||||
const rootPackageJson = JSON.parse(
|
||||
fs.readFileSync(path.join(rootDir, 'package.json'), 'utf-8'),
|
||||
);
|
||||
const corePackageJson = JSON.parse(
|
||||
fs.readFileSync(
|
||||
path.join(rootDir, 'packages', 'core', 'package.json'),
|
||||
'utf-8',
|
||||
),
|
||||
);
|
||||
|
||||
// Prepare 'cli' package
|
||||
copyFiles('cli', {
|
||||
'README.md': 'README.md',
|
||||
LICENSE: 'LICENSE',
|
||||
});
|
||||
const runtimeDependencies = {};
|
||||
if (corePackageJson.dependencies?.tiktoken) {
|
||||
runtimeDependencies.tiktoken = corePackageJson.dependencies.tiktoken;
|
||||
}
|
||||
|
||||
console.log('Successfully prepared all packages.');
|
||||
// Create a clean package.json for the published package
|
||||
const distPackageJson = {
|
||||
name: rootPackageJson.name,
|
||||
version: rootPackageJson.version,
|
||||
description:
|
||||
rootPackageJson.description || 'Qwen Code - AI-powered coding assistant',
|
||||
repository: rootPackageJson.repository,
|
||||
type: 'module',
|
||||
main: 'cli.js',
|
||||
bin: {
|
||||
qwen: 'cli.js',
|
||||
},
|
||||
files: ['cli.js', 'vendor', 'README.md', 'LICENSE'],
|
||||
config: rootPackageJson.config,
|
||||
dependencies: runtimeDependencies,
|
||||
optionalDependencies: {
|
||||
'@lydell/node-pty': '1.1.0',
|
||||
'@lydell/node-pty-darwin-arm64': '1.1.0',
|
||||
'@lydell/node-pty-darwin-x64': '1.1.0',
|
||||
'@lydell/node-pty-linux-x64': '1.1.0',
|
||||
'@lydell/node-pty-win32-arm64': '1.1.0',
|
||||
'@lydell/node-pty-win32-x64': '1.1.0',
|
||||
'node-pty': '^1.0.0',
|
||||
},
|
||||
engines: rootPackageJson.engines,
|
||||
};
|
||||
|
||||
fs.writeFileSync(
|
||||
path.join(distDir, 'package.json'),
|
||||
JSON.stringify(distPackageJson, null, 2) + '\n',
|
||||
);
|
||||
|
||||
console.log('\n✅ Package prepared for publishing at dist/');
|
||||
console.log('\nPackage structure:');
|
||||
execSync('ls -lh dist/', { stdio: 'inherit', cwd: rootDir });
|
||||
|
||||
Reference in New Issue
Block a user