-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.ts
145 lines (126 loc) · 5.14 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
import { existsSync, mkdirSync, readdirSync, writeFileSync } from 'fs'
import { join, dirname } from 'path'
// Alternative in Rust: https://github.com/silvia-odwyer/photon
import sharp from 'sharp'
import { generateAndroidAdaptiveIcons } from './adaptive-icon'
import { contentsWithLinks } from './ios'
import { Log, Options } from './types'
type Input = {
// Location of the project that has installed the plugin.
projectPath?: string
// Location of the /android and /ios folder where changes should be applied.
nativePath?: string
log?: Log
options?: Options
}
const iconSourcePaths = (projectPath: string) => [
join(projectPath, 'icon.png'),
join(projectPath, 'app-icon.png'),
join(projectPath, 'asset/icon.png'),
join(projectPath, 'logo.png'),
join(projectPath, 'icon.svg'),
join(projectPath, 'app-icon.svg'),
join(projectPath, 'asset/icon.svg'),
join(projectPath, 'logo.svg'),
]
const getInput = (projectPath: string, options: { icon?: string }) => {
if (
typeof options === 'object' &&
typeof options.icon === 'string' &&
existsSync(join(projectPath, options.icon))
) {
return join(projectPath, options.icon)
}
const paths = iconSourcePaths(projectPath)
let match: string | undefined
paths.forEach((path) => {
if (!match && existsSync(path)) {
match = path
}
})
return match
}
const getAndroidFolders = () => [
{ path: 'android/app/src/main/res/mipmap-mdpi/ic_launcher.png', size: 48 },
{ path: 'android/app/src/main/res/mipmap-hdpi/ic_launcher.png', size: 72 },
{ path: 'android/app/src/main/res/mipmap-xhdpi/ic_launcher.png', size: 96 },
{ path: 'android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png', size: 144 },
{ path: 'android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png', size: 192 },
// Round icons.
{ path: 'android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png', size: 48, round: true },
{ path: 'android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png', size: 72, round: true },
{ path: 'android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png', size: 96, round: true },
{ path: 'android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png', size: 144, round: true },
{ path: 'android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png', size: 192, round: true },
]
const getIOSFolders = (iosImageDirectory?: string) => {
if (!iosImageDirectory) {
return []
}
return [
{ path: `${iosImageDirectory}/AppIcon.appiconset/Icon-40.png`, size: 40 },
{ path: `${iosImageDirectory}/AppIcon.appiconset/Icon-58.png`, size: 58 },
{ path: `${iosImageDirectory}/AppIcon.appiconset/Icon-60.png`, size: 60 },
{ path: `${iosImageDirectory}/AppIcon.appiconset/Icon-80.png`, size: 80 },
{ path: `${iosImageDirectory}/AppIcon.appiconset/Icon-87.png`, size: 87 },
{ path: `${iosImageDirectory}/AppIcon.appiconset/Icon-120.png`, size: 120 },
{ path: `${iosImageDirectory}/AppIcon.appiconset/Icon-180.png`, size: 180 },
{ path: `${iosImageDirectory}/AppIcon.appiconset/Icon-1024.png`, size: 1024 },
]
}
const getSizes = (nativePath: string, log: Log) => {
const iosDirectories = readdirSync(join(nativePath, 'ios'), { withFileTypes: true })
.filter((dirent) => dirent.isDirectory())
.filter((dirent) => existsSync(join(nativePath, 'ios', dirent.name, 'Images.xcassets')))
.map((dirent) => dirent.name)
const iosImageDirectory =
iosDirectories.length > 0 ? join('ios', iosDirectories[0], 'Images.xcassets') : undefined
if (!iosImageDirectory) {
log('iOS project directory with "Images.xcassets" not found', 'warning')
}
return {
android: getAndroidFolders(),
ios: getIOSFolders(iosImageDirectory),
iosDirectory: iosImageDirectory,
}
}
export default async ({
projectPath = process.cwd(),
nativePath = process.cwd(),
// eslint-disable-next-line no-console
log = console.log,
options = {},
}: Input) => {
const inputFile = getInput(projectPath, options)
const sizes = getSizes(nativePath, log)
const androidPromises = sizes.android.map((icon) => {
const destinationFile = join(nativePath, icon.path)
const directory = dirname(destinationFile)
if (!existsSync(directory)) {
mkdirSync(directory, { recursive: true })
}
return sharp(inputFile).resize(icon.size, icon.size).toFile(destinationFile)
})
await Promise.all(androidPromises)
await generateAndroidAdaptiveIcons(nativePath, projectPath, options, log)
const iosPromises = sizes.ios.map((icon) => {
const destinationFile = join(nativePath, icon.path)
const directory = dirname(destinationFile)
if (!existsSync(directory)) {
mkdirSync(directory, { recursive: true })
}
// iOS doesn't support transparent icons and will add a black background, this plugin by default adds a white background.
return sharp(inputFile)
.flatten({ background: options.iOSBackground ?? '#FFFFFF' })
.resize(icon.size, icon.size)
.toFile(destinationFile)
})
await Promise.all(iosPromises)
if (sizes.iosDirectory) {
// Link ios icons in Contents.json.
writeFileSync(
join(nativePath, sizes.iosDirectory, 'AppIcon.appiconset/Contents.json'),
JSON.stringify(contentsWithLinks, null, 2),
)
}
}