Skip to content

Commit

Permalink
feat(icon): adapt to new interface and add SVG support
Browse files Browse the repository at this point in the history
release-npm
  • Loading branch information
tobua committed Jul 1, 2022
1 parent 3827938 commit 1a37720
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 28 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ npm i --save-dev icon-numic-plugin

Numic automatically picks up the plugin once installed and adds the various icons to the native folders in `/android` and `/ios` without any changes to commit. The only thing **required is an icon** of the recommended size 1024x1024. The plugin will look for icons in the following locations and pick the first match:

- icon.png
- app-icon.png
- asset/icon.png
- logo.png (also used as Avatar in SourceTree)
- icon.png / icon.svg
- app-icon.png / app-icon.svg
- asset/icon.png / asset/icon.svg
- logo.png / logo.svg (also used as Avatar in SourceTree)

## Configuration

Expand Down
50 changes: 27 additions & 23 deletions index.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,39 @@
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 { contentsWithLinks } from './ios'

// Sharp Docs: https://sharp.pixelplumbing.com/api-constructor
// Alternative: https://github.com/silvia-odwyer/photon
// https://github.com/aeirola/react-native-svg-app-icon
// https://www.npmjs.com/package/app-icon (requires imagemagik)

type Input = {
cwd?: string
projectPath?: string
nativePath?: string
log?: (message: string, type?: string) => void
options?: object
}

const iconSourcePaths = (cwd: string) => [
join(cwd, 'icon.png'),
join(cwd, 'app-icon.png'),
join(cwd, 'asset/icon.png'),
join(cwd, 'logo.png'),
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 = (cwd: string, options: { icon?: string }) => {
const getInput = (projectPath: string, options: { icon?: string }) => {
if (
typeof options === 'object' &&
typeof options.icon === 'string' &&
existsSync(join(cwd, options.icon))
existsSync(join(projectPath, options.icon))
) {
return join(cwd, options.icon)
return join(projectPath, options.icon)
}

const paths = iconSourcePaths(cwd)
const paths = iconSourcePaths(projectPath)
let match: string | undefined

paths.forEach((path) => {
Expand Down Expand Up @@ -74,10 +77,10 @@ const getIOSFolders = (iosImageDirectory: string) => {
]
}

const getSizes = ({ cwd, log }: Input) => {
const iosDirectories = readdirSync(join(cwd, 'ios'), { withFileTypes: true })
const getSizes = ({ nativePath, log }: Input) => {
const iosDirectories = readdirSync(join(nativePath, 'ios'), { withFileTypes: true })
.filter((dirent) => dirent.isDirectory())
.filter((dirent) => existsSync(join(cwd, 'ios', dirent.name, 'Images.xcassets')))
.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') : null
Expand All @@ -94,16 +97,17 @@ const getSizes = ({ cwd, log }: Input) => {
}

export default async ({
cwd = process.cwd(),
projectPath = process.cwd(),
nativePath = process.cwd(),
// eslint-disable-next-line no-console
log = console.log,
options,
options = {},
}: Input) => {
const inputFile = getInput(cwd, options)
const sizes = getSizes({ cwd, log, options })
const inputFile = getInput(projectPath, options)
const sizes = getSizes({ nativePath, projectPath, log, options })

const androidPromises = sizes.android.map((icon) => {
const destinationFile = join(cwd, icon.path)
const destinationFile = join(nativePath, icon.path)
const directory = dirname(destinationFile)
if (!existsSync(directory)) {
mkdirSync(directory, { recursive: true })
Expand All @@ -114,7 +118,7 @@ export default async ({
await Promise.all(androidPromises)

const iosPromises = sizes.ios.map((icon) => {
const destinationFile = join(cwd, icon.path)
const destinationFile = join(nativePath, icon.path)
const directory = dirname(destinationFile)
if (!existsSync(directory)) {
mkdirSync(directory, { recursive: true })
Expand All @@ -126,7 +130,7 @@ export default async ({

// Link ios icons in Contents.json.
writeFileSync(
join(cwd, sizes.iosDirectory, 'AppIcon.appiconset/Contents.json'),
join(nativePath, sizes.iosDirectory, 'AppIcon.appiconset/Contents.json'),
JSON.stringify(contentsWithLinks, null, 2)
)

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
"main": "dist/index.js",
"exports": {
"default": "./dist/index.js",
"package.json": "./package.json"
"./package.json": "./package.json"
},
"types": "dist/index.d.ts",
"files": [
Expand Down
21 changes: 21 additions & 0 deletions test/logo.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
64 changes: 64 additions & 0 deletions test/logo.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,67 @@ test('Icon path can be configured.', async () => {

expect(files.length).toBe(20)
})

test('Native output folder can be configured.', async () => {
prepare([packageJson('logo-native')])

const logoPath = join(process.cwd(), 'logo.png')

cpSync(join(initialCwd, 'test/logo.png'), logoPath)
mkdirSync(join(process.cwd(), '.numic/ios/numic/Images.xcassets'), { recursive: true })

expect(existsSync(logoPath)).toBe(true)

await plugin({ nativePath: join(process.cwd(), '.numic') })

const files = listFilesMatching('**/*.png')

expect(files.length).toBe(20)
expect(files.includes('.numic/android/app/src/main/res/mipmap-mdpi/ic_launcher.png')).toBe(true)
expect(files.includes('.numic/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png')).toBe(
true
)
expect(files.includes('.numic/ios/numic/Images.xcassets/AppIcon.appiconset/Icon-80.png')).toBe(
true
)
})

test('Also works with svg input file.', async () => {
prepare([packageJson('logo-svg')])

const logoPath = join(process.cwd(), 'logo.svg')

cpSync(join(initialCwd, 'test/logo.svg'), logoPath)
mkdirSync(join(process.cwd(), 'ios/numic/Images.xcassets'), { recursive: true })

expect(existsSync(logoPath)).toBe(true)

await plugin({ options: { icon: 'logo.svg' } })

const files = listFilesMatching('**/*.png')

expect(files.length).toBe(19)
expect(files.includes('android/app/src/main/res/mipmap-mdpi/ic_launcher.png')).toBe(true)
expect(files.includes('android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png')).toBe(true)
expect(files.includes('ios/numic/Images.xcassets/AppIcon.appiconset/Icon-80.png')).toBe(true)
})

test('Automatically finds svg in default paths.', async () => {
prepare([packageJson('logo-svg-default')])

const logoPath = join(process.cwd(), 'app-icon.svg')

cpSync(join(initialCwd, 'test/logo.svg'), logoPath)
mkdirSync(join(process.cwd(), 'ios/numic/Images.xcassets'), { recursive: true })

expect(existsSync(logoPath)).toBe(true)

await plugin({})

const files = listFilesMatching('**/*.png')

expect(files.length).toBe(19)
expect(files.includes('android/app/src/main/res/mipmap-mdpi/ic_launcher.png')).toBe(true)
expect(files.includes('android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png')).toBe(true)
expect(files.includes('ios/numic/Images.xcassets/AppIcon.appiconset/Icon-80.png')).toBe(true)
})

0 comments on commit 1a37720

Please sign in to comment.