Compare commits
3 Commits
feat/bundl
...
v0.18.0-rc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf2c06697a | ||
|
|
727f1ec4eb | ||
|
|
648353794e |
5
.changeset/icy-dryers-hunt.md
Normal file
5
.changeset/icy-dryers-hunt.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"task-master-ai": patch
|
||||
---
|
||||
|
||||
Call rules interactive setup during init
|
||||
5
.changeset/nasty-berries-tan.md
Normal file
5
.changeset/nasty-berries-tan.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"task-master-ai": patch
|
||||
---
|
||||
|
||||
Fix issues with task creation/update where subtasks are being created like id: <parent_task>.<subtask> instead if just id: <subtask>
|
||||
5
.changeset/sour-lions-check.md
Normal file
5
.changeset/sour-lions-check.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"task-master-ai": patch
|
||||
---
|
||||
|
||||
Store tasks in Git by default
|
||||
@@ -33,7 +33,7 @@ export function registerInitializeProjectTool(server) {
|
||||
storeTasksInGit: z
|
||||
.boolean()
|
||||
.optional()
|
||||
.default(false)
|
||||
.default(true)
|
||||
.describe('Store tasks in Git (tasks.json and tasks/ directory).'),
|
||||
yes: z
|
||||
.boolean()
|
||||
|
||||
1671
package-lock.json
generated
1671
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
35
package.json
35
package.json
@@ -1,13 +1,13 @@
|
||||
{
|
||||
"name": "task-master-ai",
|
||||
"version": "0.17.1-test",
|
||||
"version": "0.17.1",
|
||||
"description": "A task management system for ambitious AI-driven development that doesn't overwhelm and confuse Cursor.",
|
||||
"main": "index.js",
|
||||
"type": "module",
|
||||
"bin": {
|
||||
"task-master": "dist/task-master.cjs",
|
||||
"task-master-mcp": "dist/task-master-mcp.cjs",
|
||||
"task-master-ai": "dist/task-master-mcp.cjs"
|
||||
"task-master": "bin/task-master.js",
|
||||
"task-master-mcp": "mcp-server/server.js",
|
||||
"task-master-ai": "mcp-server/server.js"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "node --experimental-vm-modules node_modules/.bin/jest",
|
||||
@@ -22,13 +22,7 @@
|
||||
"inspector": "npx @modelcontextprotocol/inspector node mcp-server/server.js",
|
||||
"mcp-server": "node mcp-server/server.js",
|
||||
"format-check": "biome format .",
|
||||
"format": "biome format . --write",
|
||||
"prebuild": "npm test",
|
||||
"build": "vite build && npm run postbuild",
|
||||
"postbuild": "node scripts/add-shebang.js",
|
||||
"build:bundle": "npm run build",
|
||||
"build:watch": "vite build --watch",
|
||||
"test:build": "echo 'Testing bundled binaries:' && echo '🔧 CLI (task-master):' && node dist/task-master.cjs --version && echo '🔌 MCP (task-master-ai):' && node dist/task-master-mcp.cjs --help | head -5 && echo '✅ Both bundles work!'"
|
||||
"format": "biome format . --write"
|
||||
},
|
||||
"keywords": [
|
||||
"claude",
|
||||
@@ -97,11 +91,14 @@
|
||||
"url": "https://github.com/eyaltoledano/claude-task-master/issues"
|
||||
},
|
||||
"files": [
|
||||
"dist/**",
|
||||
"mcp-server/**",
|
||||
"README-task-master.md",
|
||||
"scripts/**",
|
||||
"assets/**",
|
||||
".cursor/**",
|
||||
"assets/**"
|
||||
"README-task-master.md",
|
||||
"index.js",
|
||||
"bin/**",
|
||||
"mcp-server/**",
|
||||
"src/**"
|
||||
],
|
||||
"overrides": {
|
||||
"node-fetch": "^2.6.12",
|
||||
@@ -111,19 +108,15 @@
|
||||
"@biomejs/biome": "^1.9.4",
|
||||
"@changesets/changelog-github": "^0.5.1",
|
||||
"@changesets/cli": "^2.28.1",
|
||||
"@rollup/plugin-json": "^6.1.0",
|
||||
"@rollup/plugin-node-resolve": "^16.0.1",
|
||||
"@types/jest": "^29.5.14",
|
||||
"execa": "^8.0.1",
|
||||
"ink": "^5.0.1",
|
||||
"jest": "^29.7.0",
|
||||
"jest-environment-node": "^29.7.0",
|
||||
"mock-fs": "^5.5.0",
|
||||
"pkg": "^5.8.1",
|
||||
"prettier": "^3.5.3",
|
||||
"react": "^18.3.1",
|
||||
"supertest": "^7.1.0",
|
||||
"tsx": "^4.16.2",
|
||||
"vite": "^6.3.5"
|
||||
"tsx": "^4.16.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import {
|
||||
readFileSync,
|
||||
writeFileSync,
|
||||
chmodSync,
|
||||
copyFileSync,
|
||||
mkdirSync,
|
||||
existsSync
|
||||
} from 'fs';
|
||||
import { join, dirname } from 'path';
|
||||
|
||||
const bundlePaths = [
|
||||
join(process.cwd(), 'dist/task-master.cjs'), // CLI tool
|
||||
join(process.cwd(), 'dist/task-master-mcp.cjs') // MCP server
|
||||
];
|
||||
|
||||
try {
|
||||
// Copy necessary asset files to dist
|
||||
const assetsToCopy = [
|
||||
{
|
||||
src: 'scripts/modules/supported-models.json',
|
||||
dest: 'dist/supported-models.json'
|
||||
},
|
||||
{ src: 'README-task-master.md', dest: 'dist/README-task-master.md' }
|
||||
];
|
||||
|
||||
console.log('📁 Copying assets...');
|
||||
for (const asset of assetsToCopy) {
|
||||
const srcPath = join(process.cwd(), asset.src);
|
||||
const destPath = join(process.cwd(), asset.dest);
|
||||
|
||||
if (existsSync(srcPath)) {
|
||||
// Ensure destination directory exists
|
||||
const destDir = dirname(destPath);
|
||||
if (!existsSync(destDir)) {
|
||||
mkdirSync(destDir, { recursive: true });
|
||||
}
|
||||
|
||||
copyFileSync(srcPath, destPath);
|
||||
console.log(` ✅ Copied ${asset.src} → ${asset.dest}`);
|
||||
} else {
|
||||
console.log(` ⚠️ Source not found: ${asset.src}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Process each bundle file
|
||||
for (const bundlePath of bundlePaths) {
|
||||
const fileName = bundlePath.split('/').pop();
|
||||
|
||||
if (!existsSync(bundlePath)) {
|
||||
console.log(`⚠️ Bundle not found: ${fileName}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Read the existing bundle
|
||||
const bundleContent = readFileSync(bundlePath, 'utf8');
|
||||
|
||||
// Add shebang if it doesn't already exist
|
||||
if (!bundleContent.startsWith('#!/usr/bin/env node')) {
|
||||
const contentWithShebang = '#!/usr/bin/env node\n' + bundleContent;
|
||||
writeFileSync(bundlePath, contentWithShebang);
|
||||
console.log(`✅ Added shebang to ${fileName}`);
|
||||
} else {
|
||||
console.log(`✅ Shebang already exists in ${fileName}`);
|
||||
}
|
||||
|
||||
// Make it executable
|
||||
chmodSync(bundlePath, 0o755);
|
||||
console.log(`✅ Made ${fileName} executable`);
|
||||
}
|
||||
|
||||
console.log('📦 Both bundles ready:');
|
||||
console.log(' 🔧 CLI tool: dist/task-master.cjs');
|
||||
console.log(' 🔌 MCP server: dist/task-master-mcp.cjs');
|
||||
console.log('🧪 Test with:');
|
||||
console.log(' node dist/task-master.cjs --version');
|
||||
console.log(' node dist/task-master-mcp.cjs --help');
|
||||
} catch (error) {
|
||||
console.error('❌ Post-build failed:', error.message);
|
||||
process.exit(1);
|
||||
}
|
||||
106
scripts/init.js
106
scripts/init.js
@@ -352,10 +352,30 @@ async function initializeProject(options = {}) {
|
||||
// console.log('Skip prompts determined:', skipPrompts);
|
||||
// }
|
||||
|
||||
const selectedRuleProfiles =
|
||||
options.rules && Array.isArray(options.rules) && options.rules.length > 0
|
||||
? options.rules
|
||||
: RULE_PROFILES; // Default to all profiles
|
||||
let selectedRuleProfiles;
|
||||
if (options.rulesExplicitlyProvided) {
|
||||
// If --rules flag was used, always respect it.
|
||||
log(
|
||||
'info',
|
||||
`Using rule profiles provided via command line: ${options.rules.join(', ')}`
|
||||
);
|
||||
selectedRuleProfiles = options.rules;
|
||||
} else if (skipPrompts) {
|
||||
// If non-interactive (e.g., --yes) and no rules specified, default to ALL.
|
||||
log(
|
||||
'info',
|
||||
`No rules specified in non-interactive mode, defaulting to all profiles.`
|
||||
);
|
||||
selectedRuleProfiles = RULE_PROFILES;
|
||||
} else {
|
||||
// If interactive and no rules specified, default to NONE.
|
||||
// The 'rules --setup' wizard will handle selection.
|
||||
log(
|
||||
'info',
|
||||
'No rules specified; interactive setup will be launched to select profiles.'
|
||||
);
|
||||
selectedRuleProfiles = [];
|
||||
}
|
||||
|
||||
if (skipPrompts) {
|
||||
if (!isSilentMode()) {
|
||||
@@ -373,7 +393,7 @@ async function initializeProject(options = {}) {
|
||||
options.addAliases !== undefined ? options.addAliases : true; // Default to true if not specified
|
||||
const initGit = options.initGit !== undefined ? options.initGit : true; // Default to true if not specified
|
||||
const storeTasksInGit =
|
||||
options.storeTasksInGit !== undefined ? options.storeTasksInGit : false; // Default to false if not specified
|
||||
options.storeTasksInGit !== undefined ? options.storeTasksInGit : true; // Default to true if not specified
|
||||
|
||||
if (dryRun) {
|
||||
log('info', 'DRY RUN MODE: No files will be modified');
|
||||
@@ -443,17 +463,17 @@ async function initializeProject(options = {}) {
|
||||
}
|
||||
|
||||
// Prompt for Git tasks storage (skip if --git-tasks or --no-git-tasks flag was provided)
|
||||
let storeGitPrompted = false; // Default to false
|
||||
let storeGitPrompted = true; // Default to true
|
||||
if (options.storeTasksInGit !== undefined) {
|
||||
storeGitPrompted = options.storeTasksInGit; // Use flag value if provided
|
||||
} else {
|
||||
const gitTasksInput = await promptQuestion(
|
||||
rl,
|
||||
chalk.cyan(
|
||||
'Store tasks in Git (tasks.json and tasks/ directory)? (y/N): '
|
||||
'Store tasks in Git (tasks.json and tasks/ directory)? (Y/n): '
|
||||
)
|
||||
);
|
||||
storeGitPrompted = gitTasksInput.trim().toLowerCase() === 'y';
|
||||
storeGitPrompted = gitTasksInput.trim().toLowerCase() !== 'n';
|
||||
}
|
||||
|
||||
// Confirm settings...
|
||||
@@ -492,16 +512,6 @@ async function initializeProject(options = {}) {
|
||||
'info',
|
||||
`Using rule profiles provided via command line: ${selectedRuleProfiles.join(', ')}`
|
||||
);
|
||||
} else {
|
||||
try {
|
||||
const targetDir = process.cwd();
|
||||
execSync('npx task-master rules setup', {
|
||||
stdio: 'inherit',
|
||||
cwd: targetDir
|
||||
});
|
||||
} catch (error) {
|
||||
log('error', 'Failed to run interactive rules setup:', error.message);
|
||||
}
|
||||
}
|
||||
|
||||
const dryRun = options.dryRun || false;
|
||||
@@ -541,7 +551,9 @@ async function initializeProject(options = {}) {
|
||||
);
|
||||
rl.close();
|
||||
} catch (error) {
|
||||
rl.close();
|
||||
if (rl) {
|
||||
rl.close();
|
||||
}
|
||||
log('error', `Error during initialization process: ${error.message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
@@ -564,7 +576,7 @@ function createProjectStructure(
|
||||
storeTasksInGit,
|
||||
dryRun,
|
||||
options,
|
||||
selectedRuleProfiles = RULE_PROFILES // Default to all rule profiles
|
||||
selectedRuleProfiles = RULE_PROFILES
|
||||
) {
|
||||
const targetDir = process.cwd();
|
||||
log('info', `Initializing project in ${targetDir}`);
|
||||
@@ -665,10 +677,13 @@ function createProjectStructure(
|
||||
log('warn', 'Git not available, skipping repository initialization');
|
||||
}
|
||||
|
||||
// Generate profile rules from assets/rules
|
||||
log('info', 'Generating profile rules from assets/rules...');
|
||||
for (const profileName of selectedRuleProfiles) {
|
||||
_processSingleProfile(profileName);
|
||||
// Only run the manual transformer if rules were provided via flags.
|
||||
// The interactive `rules --setup` wizard handles its own installation.
|
||||
if (options.rulesExplicitlyProvided || options.yes) {
|
||||
log('info', 'Generating profile rules from command-line flags...');
|
||||
for (const profileName of selectedRuleProfiles) {
|
||||
_processSingleProfile(profileName);
|
||||
}
|
||||
}
|
||||
|
||||
// Add shell aliases if requested
|
||||
@@ -699,6 +714,49 @@ function createProjectStructure(
|
||||
);
|
||||
}
|
||||
|
||||
// === Add Rule Profiles Setup Step ===
|
||||
if (
|
||||
!isSilentMode() &&
|
||||
!dryRun &&
|
||||
!options?.yes &&
|
||||
!options.rulesExplicitlyProvided
|
||||
) {
|
||||
console.log(
|
||||
boxen(chalk.cyan('Configuring Rule Profiles...'), {
|
||||
padding: 0.5,
|
||||
margin: { top: 1, bottom: 0.5 },
|
||||
borderStyle: 'round',
|
||||
borderColor: 'blue'
|
||||
})
|
||||
);
|
||||
log(
|
||||
'info',
|
||||
'Running interactive rules setup. Please select which rule profiles to include.'
|
||||
);
|
||||
try {
|
||||
// Correct command confirmed by you.
|
||||
execSync('npx task-master rules --setup', {
|
||||
stdio: 'inherit',
|
||||
cwd: targetDir
|
||||
});
|
||||
log('success', 'Rule profiles configured.');
|
||||
} catch (error) {
|
||||
log('error', 'Failed to configure rule profiles:', error.message);
|
||||
log('warn', 'You may need to run "task-master rules --setup" manually.');
|
||||
}
|
||||
} else if (isSilentMode() || dryRun || options?.yes) {
|
||||
// This branch can log why setup was skipped, similar to the model setup logic.
|
||||
if (options.rulesExplicitlyProvided) {
|
||||
log(
|
||||
'info',
|
||||
'Skipping interactive rules setup because --rules flag was used.'
|
||||
);
|
||||
} else {
|
||||
log('info', 'Skipping interactive rules setup in non-interactive mode.');
|
||||
}
|
||||
}
|
||||
// =====================================
|
||||
|
||||
// === Add Model Configuration Step ===
|
||||
if (!isSilentMode() && !dryRun && !options?.yes) {
|
||||
console.log(
|
||||
|
||||
@@ -3828,7 +3828,26 @@ Examples:
|
||||
if (options[RULES_SETUP_ACTION]) {
|
||||
// Run interactive rules setup ONLY (no project init)
|
||||
const selectedRuleProfiles = await runInteractiveProfilesSetup();
|
||||
for (const profile of selectedRuleProfiles) {
|
||||
|
||||
if (!selectedRuleProfiles || selectedRuleProfiles.length === 0) {
|
||||
console.log(chalk.yellow('No profiles selected. Exiting.'));
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(
|
||||
chalk.blue(
|
||||
`Installing ${selectedRuleProfiles.length} selected profile(s)...`
|
||||
)
|
||||
);
|
||||
|
||||
for (let i = 0; i < selectedRuleProfiles.length; i++) {
|
||||
const profile = selectedRuleProfiles[i];
|
||||
console.log(
|
||||
chalk.blue(
|
||||
`Processing profile ${i + 1}/${selectedRuleProfiles.length}: ${profile}...`
|
||||
)
|
||||
);
|
||||
|
||||
if (!isValidProfile(profile)) {
|
||||
console.warn(
|
||||
`Rule profile for "${profile}" not found. Valid profiles: ${RULE_PROFILES.join(', ')}. Skipping.`
|
||||
@@ -3836,16 +3855,20 @@ Examples:
|
||||
continue;
|
||||
}
|
||||
const profileConfig = getRulesProfile(profile);
|
||||
|
||||
const addResult = convertAllRulesToProfileRules(
|
||||
projectDir,
|
||||
profileConfig
|
||||
);
|
||||
if (typeof profileConfig.onAddRulesProfile === 'function') {
|
||||
profileConfig.onAddRulesProfile(projectDir);
|
||||
}
|
||||
|
||||
console.log(chalk.green(generateProfileSummary(profile, addResult)));
|
||||
}
|
||||
|
||||
console.log(
|
||||
chalk.green(
|
||||
`\nCompleted installation of all ${selectedRuleProfiles.length} profile(s).`
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,23 @@ const updatedTaskSchema = z
|
||||
priority: z.string().optional(),
|
||||
details: z.string().optional(),
|
||||
testStrategy: z.string().optional(),
|
||||
subtasks: z.array(z.any()).optional()
|
||||
subtasks: z
|
||||
.array(
|
||||
z.object({
|
||||
id: z
|
||||
.number()
|
||||
.int()
|
||||
.positive()
|
||||
.describe('Sequential subtask ID starting from 1'),
|
||||
title: z.string(),
|
||||
description: z.string(),
|
||||
status: z.string(),
|
||||
dependencies: z.array(z.number().int()).optional(),
|
||||
details: z.string().optional(),
|
||||
testStrategy: z.string().optional()
|
||||
})
|
||||
)
|
||||
.optional()
|
||||
})
|
||||
.strip(); // Allows parsing even if AI adds extra fields, but validation focuses on schema
|
||||
|
||||
@@ -441,6 +457,8 @@ Guidelines:
|
||||
9. Instead, add a new subtask that clearly indicates what needs to be changed or replaced
|
||||
10. Use the existence of completed subtasks as an opportunity to make new subtasks more specific and targeted
|
||||
11. Ensure any new subtasks have unique IDs that don't conflict with existing ones
|
||||
12. CRITICAL: For subtask IDs, use ONLY numeric values (1, 2, 3, etc.) NOT strings ("1", "2", "3")
|
||||
13. CRITICAL: Subtask IDs should start from 1 and increment sequentially (1, 2, 3...) - do NOT use parent task ID as prefix
|
||||
|
||||
The changes described in the prompt should be thoughtfully applied to make the task more accurate and actionable.`;
|
||||
|
||||
@@ -573,6 +591,37 @@ The changes described in the prompt should be thoughtfully applied to make the t
|
||||
);
|
||||
updatedTask.status = taskToUpdate.status;
|
||||
}
|
||||
// Fix subtask IDs if they exist (ensure they are numeric and sequential)
|
||||
if (updatedTask.subtasks && Array.isArray(updatedTask.subtasks)) {
|
||||
let currentSubtaskId = 1;
|
||||
updatedTask.subtasks = updatedTask.subtasks.map((subtask) => {
|
||||
// Fix AI-generated subtask IDs that might be strings or use parent ID as prefix
|
||||
const correctedSubtask = {
|
||||
...subtask,
|
||||
id: currentSubtaskId, // Override AI-generated ID with correct sequential ID
|
||||
dependencies: Array.isArray(subtask.dependencies)
|
||||
? subtask.dependencies
|
||||
.map((dep) =>
|
||||
typeof dep === 'string' ? parseInt(dep, 10) : dep
|
||||
)
|
||||
.filter(
|
||||
(depId) =>
|
||||
!Number.isNaN(depId) &&
|
||||
depId >= 1 &&
|
||||
depId < currentSubtaskId
|
||||
)
|
||||
: [],
|
||||
status: subtask.status || 'pending'
|
||||
};
|
||||
currentSubtaskId++;
|
||||
return correctedSubtask;
|
||||
});
|
||||
report(
|
||||
'info',
|
||||
`Fixed ${updatedTask.subtasks.length} subtask IDs to be sequential numeric IDs.`
|
||||
);
|
||||
}
|
||||
|
||||
// Preserve completed subtasks (Keep existing logic)
|
||||
if (taskToUpdate.subtasks?.length > 0) {
|
||||
if (!updatedTask.subtasks) {
|
||||
|
||||
@@ -255,7 +255,7 @@ function mergeWithExistingFile(
|
||||
function manageGitignoreFile(
|
||||
targetPath,
|
||||
content,
|
||||
storeTasksInGit = false,
|
||||
storeTasksInGit = true,
|
||||
log = null
|
||||
) {
|
||||
// Validate inputs
|
||||
|
||||
@@ -206,6 +206,7 @@ export function convertAllRulesToProfileRules(projectDir, profile) {
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
const assetsDir = path.join(__dirname, '..', '..', 'assets');
|
||||
|
||||
if (typeof profile.onPostConvertRulesProfile === 'function') {
|
||||
profile.onPostConvertRulesProfile(projectDir, assetsDir);
|
||||
}
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
import { defineConfig } from 'vite';
|
||||
import { resolve } from 'path';
|
||||
import { nodeResolve } from '@rollup/plugin-node-resolve';
|
||||
|
||||
export default defineConfig({
|
||||
build: {
|
||||
ssr: true, // Use SSR mode for Node.js
|
||||
rollupOptions: {
|
||||
// Multiple entry points for different applications
|
||||
input: {
|
||||
'task-master': resolve(__dirname, 'bin/task-master.js'), // CLI tool
|
||||
'task-master-mcp': resolve(__dirname, 'mcp-server/server.js') // MCP server
|
||||
},
|
||||
// Bundle everything except Node.js built-ins
|
||||
external: [
|
||||
// Node.js built-in modules
|
||||
'fs',
|
||||
'fs/promises',
|
||||
'path',
|
||||
'os',
|
||||
'crypto',
|
||||
'http',
|
||||
'https',
|
||||
'net',
|
||||
'tls',
|
||||
'child_process',
|
||||
'util',
|
||||
'events',
|
||||
'stream',
|
||||
'url',
|
||||
'querystring',
|
||||
'buffer',
|
||||
'module',
|
||||
'worker_threads',
|
||||
'readline',
|
||||
'process',
|
||||
'assert',
|
||||
'zlib',
|
||||
'dns',
|
||||
'perf_hooks',
|
||||
// Optional dependencies that might not be available
|
||||
'@anthropic-ai/claude-code'
|
||||
],
|
||||
output: {
|
||||
// Generate separate files for each entry
|
||||
dir: 'dist',
|
||||
format: 'cjs', // CommonJS for Node.js compatibility
|
||||
entryFileNames: '[name].cjs',
|
||||
chunkFileNames: 'chunks/[name]-[hash].cjs',
|
||||
assetFileNames: 'assets/[name].[ext]'
|
||||
},
|
||||
plugins: [
|
||||
nodeResolve({
|
||||
preferBuiltins: true,
|
||||
exportConditions: ['node']
|
||||
})
|
||||
]
|
||||
},
|
||||
target: 'node18',
|
||||
outDir: 'dist',
|
||||
minify: false, // Keep readable for debugging
|
||||
sourcemap: false
|
||||
},
|
||||
define: {
|
||||
// Define any environment variables if needed
|
||||
'process.env.NODE_ENV': '"production"'
|
||||
},
|
||||
ssr: {
|
||||
// Don't externalize any dependencies - bundle them all
|
||||
noExternal: true
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user