Skip to content

Commit

Permalink
Merge pull request #209 from nextcloud-libraries/backport/207/stable1
Browse files Browse the repository at this point in the history
[stable1] Allow to specify an output prefix and load app id from appinfo
  • Loading branch information
susnux authored Jun 20, 2024
2 parents 4e5a1ff + 6e8da97 commit b8ceeb5
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 9 deletions.
47 changes: 38 additions & 9 deletions lib/appConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,27 +7,32 @@
import type { Plugin, UserConfig, UserConfigFn } from 'vite'
import type { BaseOptions, NodePolyfillsOptions } from './baseConfig.js'

import { readFileSync } from 'node:fs'
import { relative } from 'node:path'
import { cwd } from 'node:process'
import { mergeConfig } from 'vite'
import { createBaseConfig } from './baseConfig.js'
import { findAppinfo } from './utils/appinfo.js'

import EmptyJSDirPlugin from './plugins/EmptyJSDir.js'
import replace from '@rollup/plugin-replace'
import injectCSSPlugin from 'vite-plugin-css-injected-by-js'

type VitePluginInjectCSSOptions = Parameters<typeof injectCSSPlugin>[0]

export const appVersion = process.env.npm_package_version
export const sanitizeAppName = (appName: string) => appName.replace(/[/\\]/, '-')

export interface AppOptions extends Omit<BaseOptions, 'inlineCSS'> {
/**
* Override the `appName`, by default the name from the `package.json` is used.
* Override the `appName`, by default the name from the `appinfo/info.xml` and if not found the name from `package.json` is used.
* But if that name differs from the app id used for the Nextcloud app you need to override it.
* @default process.env.npm_package_name
*/
appName?: string

/**
* Prefix to use for assets and chunks
* @default '{appName}-'
*/
assetsPrefix?: string

/**
* Inject all styles inside the javascript bundle instead of emitting a .css file
* @default false
Expand Down Expand Up @@ -72,7 +77,6 @@ export interface AppOptions extends Omit<BaseOptions, 'inlineCSS'> {
export const createAppConfig = (entries: { [entryAlias: string]: string }, options: AppOptions = {}): UserConfigFn => {
// Add default options
options = {
appName: process.env.npm_package_name,
config: {},
nodePolyfills: {
protocolImports: true,
Expand All @@ -81,11 +85,36 @@ export const createAppConfig = (entries: { [entryAlias: string]: string }, optio
...options,
}

let appVersion: string

const appinfo = findAppinfo(cwd())
if (appinfo) {
const content = String(readFileSync(appinfo))
const version = content.match(/<version>([^<]+)<\/version>/i)[1]
const id = content.match(/<id>([^<]+)<\/id>/i)[1]

if (version) {
appVersion = version
}
if (id && !options.appName) {
options.appName = id
}
} else {
appVersion = process.env.npm_package_version
}

if (!options.appName) {
console.warn('No app name configured, falling back to name from `package.json`')
options.appName = process.env.npm_package_name
}

return createBaseConfig({
...(options as BaseOptions),
config: async (env) => {
console.info(`Building ${options.appName} for ${env.mode}`)

const assetsPrefix = (options.assetsPrefix ?? `${options.appName}-`).replace(/[/\\]/, '-')

// This config is used to extend or override our base config
// Make sure we get a user config and not a promise or a user config function
const userConfig = await Promise.resolve(typeof options.config === 'function' ? options.config(env) : options.config)
Expand Down Expand Up @@ -162,17 +191,17 @@ export const createAppConfig = (entries: { [entryAlias: string]: string }, optio
if (/png|jpe?g|svg|gif|tiff|bmp|ico/i.test(extType)) {
return 'img/[name][extname]'
} else if (/css/i.test(extType)) {
return `css/${sanitizeAppName(options.appName)}-[name].css`
return `css/${assetsPrefix}[name].css`
} else if (/woff2?|ttf|otf/i.test(extType)) {
return 'css/fonts/[name][extname]'
}
return 'dist/[name]-[hash][extname]'
},
entryFileNames: () => {
return `js/${sanitizeAppName(options.appName)}-[name].mjs`
return `js/${assetsPrefix}[name].mjs`
},
chunkFileNames: () => {
return 'js/[name]-[hash].mjs'
return 'js/[name].chunk.mjs'
},
manualChunks: {
...(options?.coreJS ? { polyfill: ['core-js'] } : {}),
Expand Down
46 changes: 46 additions & 0 deletions lib/utils/appinfo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

import { lstatSync } from 'node:fs'
import { join, resolve, sep } from 'node:path'

/**
* Check if a given path exists and is a directory
*
* @param {string} filePath The path
* @return {boolean}
*/
function isDirectory(filePath: string): boolean {
const stats = lstatSync(filePath, { throwIfNoEntry: false })
return stats !== undefined && stats.isDirectory()
}

/**
* Check if a given path exists and is a directory
*
* @param {string} filePath The path
* @return {boolean}
*/
function isFile(filePath: string): boolean {
const stats = lstatSync(filePath, { throwIfNoEntry: false })
return stats !== undefined && stats.isFile()
}

/**
* Find the path of nearest `appinfo/info.xml` relative to given path
*
* @param {string} currentPath The path to check for appinfo
* @return {string|undefined} Either the full path including the `info.xml` part or `undefined` if no found
*/
export function findAppinfo(currentPath: string): string | null {
while (currentPath && currentPath !== sep) {
const appinfoPath = join(currentPath, 'appinfo')
if (isDirectory(appinfoPath) && isFile(join(appinfoPath, 'info.xml'))) {
return join(appinfoPath, 'info.xml')
}
currentPath = resolve(currentPath, '..')
}
return undefined
}

0 comments on commit b8ceeb5

Please sign in to comment.