mirror of
https://github.com/directus/directus.git
synced 2026-02-02 05:05:04 -05:00
* Add support for npm extensions * Allow extensions to import vue from the main app * Bundle app extensions on server startup * Fix return type of useLayoutState * Add shared package * Add extension-sdk package * Add type declaration files to allow deep import of shared package * Add extension loading to shared * Refactor extension loading to use shared package * Remove app bundle newline replacement * Fix extension loading in development * Rename extension entrypoints * Update extension build instructions * Remove vite auto-replacement workaround * Update package-lock.json * Remove newline from generated extension entrypoint * Update package-lock.json * Build shared package as cjs and esm * Move useLayoutState composable to shared * Reverse vite base env check * Share useLayoutState composable through extension-sdk * Update layout docs * Update package versions * Small cleanup * Fix layout docs * Fix imports * Add nickrum to codeowners * Fix typo * Add 'em to vite config too * Fix email Co-authored-by: rijkvanzanten <rijkvanzanten@me.com>
96 lines
3.1 KiB
TypeScript
96 lines
3.1 KiB
TypeScript
import path from 'path';
|
|
import fse from 'fs-extra';
|
|
import { AppExtensionType, Extension } from '../types';
|
|
import { resolvePackage } from './resolve-package';
|
|
import { listFolders } from './list-folders';
|
|
import { EXTENSION_NAME_REGEX, EXTENSION_TYPES } from '../constants';
|
|
import { pluralize } from './pluralize';
|
|
|
|
export async function getPackageExtensions(root: string): Promise<Extension[]> {
|
|
const pkg = await fse.readJSON(path.resolve(path.join(root, 'package.json')));
|
|
const extensionNames = Object.keys(pkg.dependencies).filter((dep) => EXTENSION_NAME_REGEX.test(dep));
|
|
|
|
return listExtensionsChildren(extensionNames);
|
|
|
|
async function listExtensionsChildren(extensionNames: string[], root?: string) {
|
|
const extensions: Extension[] = [];
|
|
|
|
for (const extensionName of extensionNames) {
|
|
const extensionPath = resolvePackage(extensionName, root);
|
|
const extensionPkg = await fse.readJSON(path.join(extensionPath, 'package.json'));
|
|
|
|
if (extensionPkg['directus:extension'].type === 'pack') {
|
|
const extensionChildren = Object.keys(extensionPkg.dependencies).filter((dep) =>
|
|
EXTENSION_NAME_REGEX.test(dep)
|
|
);
|
|
|
|
const extension: Extension = {
|
|
path: extensionPath,
|
|
name: extensionName,
|
|
version: extensionPkg.version,
|
|
type: extensionPkg['directus:extension'].type,
|
|
host: extensionPkg['directus:extension'].host,
|
|
children: extensionChildren,
|
|
local: false,
|
|
root: root === undefined,
|
|
};
|
|
|
|
extensions.push(extension);
|
|
extensions.push(...(await listExtensionsChildren(extension.children || [], extension.path)));
|
|
} else {
|
|
extensions.push({
|
|
path: extensionPath,
|
|
name: extensionName,
|
|
version: extensionPkg.version,
|
|
type: extensionPkg['directus:extension'].type,
|
|
entrypoint: extensionPkg['directus:extension'].path,
|
|
host: extensionPkg['directus:extension'].host,
|
|
local: false,
|
|
root: root === undefined,
|
|
});
|
|
}
|
|
}
|
|
|
|
return extensions;
|
|
}
|
|
}
|
|
|
|
export async function getLocalExtensions(root: string): Promise<Extension[]> {
|
|
const extensions: Extension[] = [];
|
|
|
|
for (const extensionType of EXTENSION_TYPES) {
|
|
const typeDir = pluralize(extensionType);
|
|
const typePath = path.resolve(path.join(root, typeDir));
|
|
|
|
try {
|
|
const extensionNames = await listFolders(typePath);
|
|
|
|
for (const extensionName of extensionNames) {
|
|
const extensionPath = path.join(typePath, extensionName);
|
|
|
|
extensions.push({
|
|
path: extensionPath,
|
|
name: extensionName,
|
|
type: extensionType,
|
|
entrypoint: 'index.js',
|
|
local: true,
|
|
root: true,
|
|
});
|
|
}
|
|
} catch (err) {
|
|
if (err.code === 'ENOENT') throw new Error(`Extension folder "${typePath}" couldn't be opened`);
|
|
throw err;
|
|
}
|
|
}
|
|
|
|
return extensions;
|
|
}
|
|
|
|
export function generateExtensionsEntry(type: AppExtensionType, extensions: Extension[]): string {
|
|
const filteredExtensions = extensions.filter((extension) => extension.type === type);
|
|
|
|
return `${filteredExtensions
|
|
.map((extension, i) => `import e${i} from '${path.resolve(extension.path, extension.entrypoint || '')}';\n`)
|
|
.join('')}export default [${filteredExtensions.map((_, i) => `e${i}`).join(',')}];`;
|
|
}
|