cross-packer 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +151 -0
  3. package/README.zh-CN.md +151 -0
  4. package/dist/archive/ar.d.ts +12 -0
  5. package/dist/archive/seven-za.d.ts +32 -0
  6. package/dist/archive/tar.d.ts +30 -0
  7. package/dist/archive/zip-reader.d.ts +17 -0
  8. package/dist/asar/dependency/collect.d.ts +2 -0
  9. package/dist/asar/dependency/detector.d.ts +8 -0
  10. package/dist/asar/dependency/hoister.d.ts +27 -0
  11. package/dist/asar/dependency/utils.d.ts +59 -0
  12. package/dist/asar/fileset/matcher.d.ts +12 -0
  13. package/dist/asar/fileset/platform-fileset.d.ts +36 -0
  14. package/dist/asar/fileset/transformer.d.ts +2 -0
  15. package/dist/asar/fileset/unpack-detector.d.ts +10 -0
  16. package/dist/asar/fileset/walker.d.ts +14 -0
  17. package/dist/asar/pack.d.ts +23 -0
  18. package/dist/asar/packer/app-collector.d.ts +2 -0
  19. package/dist/asar/packer/collector.d.ts +18 -0
  20. package/dist/asar/packer/concurrency.d.ts +1 -0
  21. package/dist/asar/packer/integrity.d.ts +8 -0
  22. package/dist/asar/packer/nm-collector.d.ts +7 -0
  23. package/dist/asar/packer/stream-builder.d.ts +18 -0
  24. package/dist/cli.d.ts +2 -0
  25. package/dist/config/index.d.ts +97 -0
  26. package/dist/electron/dist-zip.d.ts +18 -0
  27. package/dist/electron/download.d.ts +21 -0
  28. package/dist/index.d.ts +11 -0
  29. package/dist/packer/common/app-update-yml.d.ts +5 -0
  30. package/dist/packer/common/extra-resources.d.ts +39 -0
  31. package/dist/packer/common/staging.d.ts +3 -0
  32. package/dist/packer/common/template.d.ts +2 -0
  33. package/dist/packer/linux/linux.d.ts +2 -0
  34. package/dist/packer/mac/mac.d.ts +23 -0
  35. package/dist/packer/win/nsis.d.ts +24 -0
  36. package/dist/packer/win/resedit.d.ts +5 -0
  37. package/dist/packer/win/toolchain.d.ts +8 -0
  38. package/dist/packer/win/win.d.ts +12 -0
  39. package/dist/pipeline/pipeline.d.ts +41 -0
  40. package/dist/pipeline/targets.d.ts +28 -0
  41. package/dist/shared/hash.d.ts +2 -0
  42. package/dist/shared/logger.d.ts +39 -0
  43. package/dist/shared/platform.d.ts +26 -0
  44. package/dist/shared/types.d.ts +68 -0
  45. package/dist/update/blockmap.d.ts +13 -0
  46. package/dist/update/metadata.d.ts +20 -0
  47. package/dist/update/version.d.ts +7 -0
  48. package/package.json +71 -0
  49. package/src/archive/ar.ts +41 -0
  50. package/src/archive/seven-za.ts +160 -0
  51. package/src/archive/tar.ts +142 -0
  52. package/src/archive/zip-reader.ts +61 -0
  53. package/src/asar/dependency/collect.ts +437 -0
  54. package/src/asar/dependency/detector.ts +73 -0
  55. package/src/asar/dependency/hoister.ts +714 -0
  56. package/src/asar/dependency/utils.ts +225 -0
  57. package/src/asar/fileset/matcher.ts +101 -0
  58. package/src/asar/fileset/platform-fileset.ts +416 -0
  59. package/src/asar/fileset/transformer.ts +154 -0
  60. package/src/asar/fileset/unpack-detector.ts +38 -0
  61. package/src/asar/fileset/walker.ts +115 -0
  62. package/src/asar/pack.ts +186 -0
  63. package/src/asar/packer/app-collector.ts +74 -0
  64. package/src/asar/packer/collector.ts +134 -0
  65. package/src/asar/packer/concurrency.ts +24 -0
  66. package/src/asar/packer/integrity.ts +53 -0
  67. package/src/asar/packer/nm-collector.ts +194 -0
  68. package/src/asar/packer/stream-builder.ts +723 -0
  69. package/src/cli.ts +182 -0
  70. package/src/config/index.ts +365 -0
  71. package/src/electron/dist-zip.ts +65 -0
  72. package/src/electron/download.ts +121 -0
  73. package/src/index.ts +14 -0
  74. package/src/packer/common/app-update-yml.ts +21 -0
  75. package/src/packer/common/extra-resources.ts +83 -0
  76. package/src/packer/common/staging.ts +33 -0
  77. package/src/packer/common/template.ts +24 -0
  78. package/src/packer/linux/linux.ts +329 -0
  79. package/src/packer/mac/mac.ts +495 -0
  80. package/src/packer/win/nsis.ts +244 -0
  81. package/src/packer/win/resedit.ts +60 -0
  82. package/src/packer/win/toolchain.ts +63 -0
  83. package/src/packer/win/win.ts +421 -0
  84. package/src/pipeline/pipeline.ts +149 -0
  85. package/src/pipeline/targets.ts +67 -0
  86. package/src/shared/hash.ts +15 -0
  87. package/src/shared/logger.ts +91 -0
  88. package/src/shared/platform.ts +41 -0
  89. package/src/shared/types.ts +82 -0
  90. package/src/update/blockmap.ts +247 -0
  91. package/src/update/metadata.ts +97 -0
  92. package/src/update/version.ts +59 -0
  93. package/templates/linux/app.desktop +11 -0
  94. package/templates/linux/control +9 -0
  95. package/templates/linux/postinst +10 -0
  96. package/templates/linux/postrm +8 -0
  97. package/templates/linux/preinst +49 -0
  98. package/templates/linux/prerm +43 -0
  99. package/templates/mac/Info.plist +18 -0
  100. package/templates/mac/app-update.yml +4 -0
  101. package/templates/win/nsis/README.md +51 -0
  102. package/templates/win/nsis/THIRD-PARTY-NOTICES.md +20 -0
  103. package/templates/win/nsis/assistedInstaller.nsh +154 -0
  104. package/templates/win/nsis/assistedMessages.yml +368 -0
  105. package/templates/win/nsis/common.nsh +147 -0
  106. package/templates/win/nsis/empty-license.txt +1 -0
  107. package/templates/win/nsis/include/FileAssociation.nsh +123 -0
  108. package/templates/win/nsis/include/StdUtils.nsh +496 -0
  109. package/templates/win/nsis/include/StrContains.nsh +48 -0
  110. package/templates/win/nsis/include/UAC.nsh +299 -0
  111. package/templates/win/nsis/include/allowOnlyOneInstallerInstance.nsh +164 -0
  112. package/templates/win/nsis/include/extractAppPackage.nsh +138 -0
  113. package/templates/win/nsis/include/getProcessInfo.nsh +157 -0
  114. package/templates/win/nsis/include/installUtil.nsh +248 -0
  115. package/templates/win/nsis/include/installer.nsh +247 -0
  116. package/templates/win/nsis/include/nsProcess.nsh +28 -0
  117. package/templates/win/nsis/include/webPackage.nsh +64 -0
  118. package/templates/win/nsis/installSection.nsh +110 -0
  119. package/templates/win/nsis/installer.nsi +132 -0
  120. package/templates/win/nsis/messages.yml +240 -0
  121. package/templates/win/nsis/multiUser.nsh +134 -0
  122. package/templates/win/nsis/multiUserUi.nsh +230 -0
  123. package/templates/win/nsis/oneClick.nsh +19 -0
  124. package/templates/win/nsis/portable.nsi +91 -0
  125. package/templates/win/nsis/uninstaller.nsh +263 -0
  126. package/templates/win/nsis-header.nsh +57 -0
@@ -0,0 +1,225 @@
1
+ import { execFile } from 'node:child_process'
2
+ import { existsSync } from 'node:fs'
3
+ import { readFile, realpath as realpathAsync } from 'node:fs/promises'
4
+ import path from 'node:path'
5
+ import { hoist, type ShrunkNode, transformToHoisterTree } from './hoister.ts'
6
+
7
+ /** Raw dependency info extracted from a package manager's dependency graph. */
8
+ export interface RawDependencyInfo {
9
+ name: string
10
+ version: string
11
+ path?: string
12
+ _dependencies?: Record<string, string>
13
+ dependencies?: Record<string, RawDependencyInfo>
14
+ optionalDependencies?: Record<string, RawDependencyInfo>
15
+ }
16
+
17
+ /** Flattened dependency entry stored in allDependencies. */
18
+ export interface FlatDependency {
19
+ name: string
20
+ version: string
21
+ path: string
22
+ _dependencies?: Record<string, string>
23
+ dependencies?: Record<string, RawDependencyInfo>
24
+ optionalDependencies?: Record<string, RawDependencyInfo>
25
+ }
26
+
27
+ /** Node in the hoisted node_modules output tree. */
28
+ export interface HoistedNodeModule {
29
+ name: string
30
+ version: string
31
+ dir: string
32
+ dependencies?: HoistedNodeModule[]
33
+ }
34
+
35
+ export function execCommand(
36
+ command: string,
37
+ args: string[],
38
+ cwd?: string,
39
+ ): Promise<{ stdout: string; stderr: string; error: Error | null }> {
40
+ return new Promise((resolve) => {
41
+ execFile(
42
+ command,
43
+ args,
44
+ {
45
+ cwd,
46
+ shell: true,
47
+ maxBuffer: 50 * 1024 * 1024,
48
+ },
49
+ (error, stdout, stderr) => {
50
+ resolve({ stdout: stdout || '', stderr: stderr || '', error })
51
+ },
52
+ )
53
+ })
54
+ }
55
+
56
+ /** Parse JSON from a command output, tolerating surrounding log noise. */
57
+ export function extractJsonFromOutput(output: string): unknown {
58
+ const trimmed = output.trim()
59
+ try {
60
+ return JSON.parse(trimmed)
61
+ } catch {}
62
+
63
+ const bracketOpen = Math.max(trimmed.indexOf('{'), 0)
64
+ const bracketOpenSquare = Math.max(trimmed.indexOf('['), 0)
65
+ const start = Math.min(bracketOpen, bracketOpenSquare)
66
+ for (let i = start; i < trimmed.length; i++) {
67
+ const slice = trimmed.slice(start, i + 1)
68
+ try {
69
+ return JSON.parse(slice)
70
+ } catch {}
71
+ }
72
+ throw new Error('No JSON content found in output')
73
+ }
74
+
75
+ export function resolvePackageDir(nmDir: string, pkgName: string): string | null {
76
+ if (pkgName.startsWith('@')) {
77
+ const parts = pkgName.split('/')
78
+ if (parts.length < 2) return null
79
+ const dir = path.join(nmDir, parts[0], parts[1])
80
+ return existsSync(dir) ? dir : null
81
+ }
82
+ const dir = path.join(nmDir, pkgName)
83
+ return existsSync(dir) ? dir : null
84
+ }
85
+
86
+ export function isProdDependency(
87
+ depName: string,
88
+ pkg: Pick<RawDependencyInfo, '_dependencies' | 'dependencies' | 'optionalDependencies'>,
89
+ ): boolean {
90
+ const _deps = pkg._dependencies
91
+ if (_deps) return _deps[depName] != null
92
+ // Optional dependencies count as production deps when packing.
93
+ const prodDeps = { ...(pkg.dependencies || {}), ...(pkg.optionalDependencies || {}) }
94
+ return prodDeps[depName] != null
95
+ }
96
+
97
+ /** Read the root package.json and derive its locator key. */
98
+ export async function readRootPkgJson(
99
+ projectDir: string,
100
+ ): Promise<{ name: string; version: string; rootKey: string }> {
101
+ const pkgJson = JSON.parse(await readFile(path.join(projectDir, 'package.json'), 'utf8')) as {
102
+ name: string
103
+ version: string
104
+ }
105
+ return {
106
+ name: pkgJson.name,
107
+ version: pkgJson.version,
108
+ rootKey: `${pkgJson.name}@${pkgJson.version}`,
109
+ }
110
+ }
111
+
112
+ export async function runHoistPipeline(
113
+ allDependencies: Map<string, FlatDependency>,
114
+ productionGraph: Record<string, { dependencies: string[] }>,
115
+ rootKey: string,
116
+ ): Promise<{ nodeModules: HoistedNodeModule[] }> {
117
+ const hoisterTree = transformToHoisterTree(productionGraph, rootKey)
118
+ const hoistedResult = hoist(hoisterTree)
119
+ const nodeModules = await getNodeModulesFromHoisted(hoistedResult.dependencies, allDependencies)
120
+ return { nodeModules }
121
+ }
122
+
123
+ /** Detect npm's duplicated-dependency marker entries (no deps of their own). */
124
+ export function isDuplicatedNpmDependency(info: RawDependencyInfo | FlatDependency): boolean {
125
+ const _deps = info._dependencies || {}
126
+ const deps = info.dependencies || {}
127
+ return Object.keys(_deps).length > 0 && Object.keys(deps).length === 0
128
+ }
129
+
130
+ export function collectFromObjectTree(
131
+ deps: Record<string, RawDependencyInfo> | undefined,
132
+ allDependencies: Map<string, FlatDependency>,
133
+ ): void {
134
+ if (!deps) return
135
+ for (const [name, info] of Object.entries(deps)) {
136
+ if (!info?.path) continue
137
+ const pkgDir = path.resolve(info.path)
138
+ const key = `${name}@${info.version}`
139
+ const entry: FlatDependency = {
140
+ name,
141
+ version: info.version,
142
+ path: pkgDir,
143
+ _dependencies: info._dependencies,
144
+ dependencies: info.dependencies,
145
+ optionalDependencies: info.optionalDependencies,
146
+ }
147
+ if (!isDuplicatedNpmDependency(info) || !allDependencies.has(key)) {
148
+ allDependencies.set(key, entry)
149
+ }
150
+ if (info.dependencies) {
151
+ collectFromObjectTree(info.dependencies, allDependencies)
152
+ }
153
+ }
154
+ }
155
+
156
+ export function extractProdGraphFromObjectTree(
157
+ tree: RawDependencyInfo | undefined,
158
+ dependencyId: string,
159
+ productionGraph: Record<string, { dependencies: string[] }>,
160
+ allDependencies: Map<string, FlatDependency>,
161
+ ): void {
162
+ if (productionGraph[dependencyId]) return
163
+ const isDuplicateDep = tree ? isDuplicatedNpmDependency(tree) : false
164
+ const targetTree = isDuplicateDep ? allDependencies.get(dependencyId) : tree
165
+ productionGraph[dependencyId] = { dependencies: [] }
166
+ const collectedDependencies: string[] = []
167
+ const deps = targetTree?.dependencies || {}
168
+ for (const name of Object.keys(deps)) {
169
+ const depInfo = deps[name]
170
+ if (!depInfo || Object.keys(depInfo).length === 0) continue
171
+ if (!targetTree || !isProdDependency(name, targetTree)) continue
172
+ const childDependencyId = `${name}@${depInfo.version}`
173
+ if (allDependencies.has(childDependencyId)) {
174
+ extractProdGraphFromObjectTree(depInfo, childDependencyId, productionGraph, allDependencies)
175
+ }
176
+ collectedDependencies.push(childDependencyId)
177
+ }
178
+ productionGraph[dependencyId] = { dependencies: collectedDependencies }
179
+ }
180
+
181
+ export function buildProductionGraphFromFlat(
182
+ allDependencies: Map<string, FlatDependency>,
183
+ productionGraph: Record<string, { dependencies: string[] }>,
184
+ rootKey: string,
185
+ ): void {
186
+ productionGraph[rootKey] = { dependencies: Array.from(allDependencies.keys()) }
187
+ for (const [key] of allDependencies) {
188
+ if (!productionGraph[key]) {
189
+ productionGraph[key] = { dependencies: [] }
190
+ }
191
+ }
192
+ }
193
+
194
+ /** Split a "name@version" identifier into its parts. */
195
+ export function parseNameVersion(identifier: string): { name: string; version: string } {
196
+ const lastAt = identifier.lastIndexOf('@')
197
+ if (lastAt <= 0) return { name: identifier, version: 'unknown' }
198
+ const name = identifier.slice(0, lastAt)
199
+ const version = identifier.slice(lastAt + 1)
200
+ return { name, version }
201
+ }
202
+
203
+ async function getNodeModulesFromHoisted(
204
+ dependencies: Set<ShrunkNode>,
205
+ allDependencies: Map<string, FlatDependency>,
206
+ result: HoistedNodeModule[] = [],
207
+ ): Promise<HoistedNodeModule[]> {
208
+ if (dependencies.size === 0) return result
209
+ for (const d of dependencies) {
210
+ const reference = [...d.references][0]
211
+ const key = `${d.name}@${reference}`
212
+ const depInfo = allDependencies.get(key)
213
+ if (depInfo === undefined || depInfo.path === undefined) continue
214
+ const resolvedPath = await realpathAsync(depInfo.path).catch(() => depInfo.path)
215
+ if (!existsSync(resolvedPath)) continue
216
+ const node: HoistedNodeModule = { name: d.name, version: reference, dir: resolvedPath }
217
+ result.push(node)
218
+ if (d.dependencies.size > 0) {
219
+ node.dependencies = []
220
+ await getNodeModulesFromHoisted(d.dependencies, allDependencies, node.dependencies)
221
+ }
222
+ }
223
+ result.sort((a, b) => a.name.localeCompare(b.name))
224
+ return result
225
+ }
@@ -0,0 +1,101 @@
1
+ import path from 'node:path'
2
+ import type { Minimatch } from 'minimatch'
3
+ import { EntryKind, type FileFilter, type FileStat } from '../../shared/types.ts'
4
+
5
+ export const excludedNames =
6
+ '.git,.hg,.svn,CVS,RCS,SCCS,' +
7
+ '__pycache__,.DS_Store,thumbs.db,.gitignore,.gitkeep,.gitattributes,.npmignore,' +
8
+ '.idea,.vs,.flowconfig,.jshintrc,.eslintrc,.circleci,' +
9
+ '.yarn-integrity,.yarn-metadata.json,yarn-error.log,yarn.lock,package-lock.json,npm-debug.log,pnpm-lock.yaml,bun.lock,bun.lockb,' +
10
+ 'appveyor.yml,.travis.yml,circle.yml,.nyc_output,.husky,.github,electron-builder.env'
11
+
12
+ export const excludedExts =
13
+ 'iml,hprof,orig,pyc,pyo,rbc,swp,csproj,sln,suo,xproj,cc,d.ts,' + 'mk,a,o,obj,forge-meta'
14
+
15
+ export const excludedFiles = new Set(
16
+ [
17
+ '.DS_Store',
18
+ 'node_modules',
19
+ 'CHANGELOG.md',
20
+ 'ChangeLog',
21
+ 'changelog.md',
22
+ 'Changelog.md',
23
+ 'Changelog',
24
+ 'binding.gyp',
25
+ '.npmignore',
26
+ 'node_gyp_bins',
27
+ ].concat(excludedNames.split(',')),
28
+ )
29
+
30
+ export const topLevelExcludedFiles = new Set([
31
+ 'karma.conf.js',
32
+ '.coveralls.yml',
33
+ 'README.md',
34
+ 'readme.markdown',
35
+ 'README',
36
+ 'readme.md',
37
+ 'Readme.md',
38
+ 'Readme',
39
+ 'readme',
40
+ 'test',
41
+ 'tests',
42
+ '__tests__',
43
+ 'powered-test',
44
+ 'example',
45
+ 'examples',
46
+ '.bin',
47
+ ])
48
+
49
+ export function hasMagic(pattern: Minimatch): boolean {
50
+ const set = pattern.set
51
+ if (set.length > 1) return true
52
+ for (const i of set[0]) {
53
+ if (typeof i !== 'string') return true
54
+ }
55
+ return false
56
+ }
57
+
58
+ /** Ensure a path string ends with the platform separator. */
59
+ export function ensureEndSlash(s: string): string {
60
+ return s.length === 0 || s.endsWith(path.sep) ? s : s + path.sep
61
+ }
62
+
63
+ export function getRelativePath(file: string, srcWithEndSlash: string, stat: FileStat): string {
64
+ let relative = stat.moduleFullFilePath || file.substring(srcWithEndSlash.length)
65
+ if (path.sep === '\\') {
66
+ if (relative.startsWith('\\')) {
67
+ relative = relative.substring(1)
68
+ }
69
+ relative = relative.replace(/\\/g, '/')
70
+ }
71
+ return relative
72
+ }
73
+
74
+ export function minimatchAll(p: string, patterns: Minimatch[], stat: FileStat): boolean {
75
+ let match = false
76
+ for (const pattern of patterns) {
77
+ if (match !== pattern.negate) continue
78
+ match = pattern.match(p, stat.type === EntryKind.DIRECTORY && !pattern.negate)
79
+ }
80
+ return match
81
+ }
82
+
83
+ export function createFilter(
84
+ src: string,
85
+ patterns: Minimatch[],
86
+ excludePatterns?: Minimatch[] | null,
87
+ ): FileFilter {
88
+ const srcWithEndSlash = ensureEndSlash(src)
89
+ return (file, stat) => {
90
+ if (src === file) return true
91
+ let relative = getRelativePath(file, srcWithEndSlash, stat)
92
+ if (relative === 'node_modules') return false
93
+ if (relative.endsWith('/node_modules')) relative += '/'
94
+ return (
95
+ minimatchAll(relative, patterns, stat) &&
96
+ (excludePatterns == null ||
97
+ stat.type === EntryKind.DIRECTORY ||
98
+ !minimatchAll(relative, excludePatterns, stat))
99
+ )
100
+ }
101
+ }