{"record":{"id":"47275ec81b4225f9","repo":"laurent22/joplin","slug":"plugin-archive-was-not-created-because-the-dist","errorCode":null,"errorMessage":"Plugin archive was not created because the \"dist\" directory is empty","messagePattern":"Plugin archive was not created because the \"dist\" directory is empty","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/generator-joplin/generators/app/templates/webpack.config.js","lineNumber":135,"sourceCode":"\t\tconst fileSize = fs.statSync(screenshotPath).size / 1024;\n\t\tif (fileSize > fileMaxSize) throw new Error(`Max screenshot file size is ${fileMaxSize}KB. ${screenshotPath} is ${fileSize}KB`);\n\t}\n}\n\nfunction readManifest(manifestPath) {\n\tconst content = fs.readFileSync(manifestPath, 'utf8');\n\tconst output = JSON.parse(content);\n\tif (!output.id) throw new Error(`Manifest plugin ID is not set in ${manifestPath}`);\n\tvalidateCategories(output.categories);\n\tvalidateScreenshots(output.screenshots);\n\treturn output;\n}\n\nfunction createPluginArchive(sourceDir, destPath) {\n\tconst distFiles = glob.sync(`${sourceDir}/**/*`, { nodir: true, windowsPathsNoEscape: true })\n\t\t.map(f => f.substr(sourceDir.length + 1));\n\n\tif (!distFiles.length) throw new Error('Plugin archive was not created because the \"dist\" directory is empty');\n\tfs.removeSync(destPath);\n\n\ttar.create(\n\t\t{\n\t\t\tstrict: true,\n\t\t\tportable: true,\n\t\t\tfile: destPath,\n\t\t\tcwd: sourceDir,\n\t\t\tsync: true,\n\t\t},\n\t\tdistFiles,\n\t);\n\n\tconsole.info(chalk.cyan(`Plugin archive has been created in ${destPath}`));\n}\n\nconst writeManifest = (manifestPath, content) => {\n\tfs.writeFileSync(manifestPath, JSON.stringify(content, null, '\\t'), 'utf8');","sourceCodeStart":117,"sourceCodeEnd":153,"githubUrl":"https://github.com/laurent22/joplin/blob/2654b33620775080d1d59c552259d41e33dad3d2/packages/generator-joplin/generators/app/templates/webpack.config.js#L117-L153","documentation":"Thrown by createPluginArchive() when glob.sync over `${sourceDir}/**/*` returns zero files. The dist directory is expected to contain the built plugin output before archiving into the .jpl tar. An empty dist means there is nothing to ship.","triggerScenarios":"createPluginArchive(distDir, destPath) is called but distDir contains no files — glob with nodir:true returns an empty array.","commonSituations":"Build script ran `npm run dist` before `npm run build`; webpack build failed silently or wrote to a different output dir; dist was cleaned and never rebuilt; misconfigured output.path in webpack.","solutions":["Run the build step first (e.g. `npm run build` / `yarn build`) so dist/ is populated before archiving.","Verify dist/ actually contains the emitted index.js and assets (ls dist).","Check webpack output.path matches the sourceDir passed to createPluginArchive.","If the build step failed, fix the upstream error before re-running dist."],"exampleFix":"# before\nnpm run dist   # dist/ is empty\n# after\nnpm run build && npm run dist","handlingStrategy":"validation","validationCode":"const fs = require('fs');\nconst path = require('path');\nconst hasFiles = (dir) => fs.existsSync(dir) && fs.readdirSync(dir).some(f => fs.statSync(path.join(dir, f)).isFile());\nif (!hasFiles(distDir)) throw new Error('dist/ is empty — run the build step first');","typeGuard":"const distHasOutput = (dir) => fs.existsSync(dir) && fs.readdirSync(dir).length > 0;","tryCatchPattern":"try { createPluginArchive(distDir, destPath); }\ncatch (e) { if (/\"dist\" directory is empty/.test(e.message)) { /* run build, then retry */ } else throw e; }","preventionTips":["Sequence npm scripts so `build` always runs before `dist`.","Fail the dist script early with a clearer message if dist/ is empty, before reaching createPluginArchive."],"tags":["plugin-build","archive","build-pipeline","generator-joplin"],"backgroundTag":null,"analyzedSha":"2654b33620775080d1d59c552259d41e33dad3d2","analyzedAt":"2026-08-12T14:26:46.263Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}