From 2cd439febfaed7b4c7bbadc1e210c39ed85bab5c Mon Sep 17 00:00:00 2001 From: Eugene Date: Mon, 4 Jan 2021 19:56:05 +0300 Subject: [PATCH] fix: fix resolving nested themes --- src/cli/build.ts | 2 +- src/core/build.ts | 2 +- src/core/load-theme.ts | 79 ------------------------------------------ src/core/loadTheme.ts | 70 +++++++++++++++++++++++++++++++++++++ src/core/utils.ts | 4 --- src/findPackageRoot.ts | 14 ++++++++ src/resolveFrom.ts | 30 ++++++++++++++++ 7 files changed, 116 insertions(+), 85 deletions(-) delete mode 100644 src/core/load-theme.ts create mode 100644 src/core/loadTheme.ts create mode 100644 src/findPackageRoot.ts create mode 100644 src/resolveFrom.ts diff --git a/src/cli/build.ts b/src/cli/build.ts index 4fe0014..1a6cbad 100644 --- a/src/cli/build.ts +++ b/src/cli/build.ts @@ -5,7 +5,7 @@ import chalk from 'chalk' import { loadConfig } from '../core/config' import { build } from '../core/build' -import { loadTheme } from '../core/load-theme' +import { loadTheme } from '../core/loadTheme' import { debounce, flatten } from '../core/utils' type Flags = { diff --git a/src/core/build.ts b/src/core/build.ts index 0202caa..1d968c4 100644 --- a/src/core/build.ts +++ b/src/core/build.ts @@ -7,7 +7,7 @@ import { Api, InternalApi } from '../index' import { createStyleDictionaryConfig } from './style-dictionary-config' import { variablesWithPrefix } from './variablesWithPrefix' import { loadMappers } from './mappers' -import { loadTheme } from './load-theme' +import { loadTheme } from './loadTheme' import { dedupeProps } from './dedupe-props' import { loadSources } from './load-sources' import { Config } from './config' diff --git a/src/core/load-theme.ts b/src/core/load-theme.ts deleted file mode 100644 index 3701abc..0000000 --- a/src/core/load-theme.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { join, resolve, dirname } from 'path' -import { readJSON } from 'fs-extra' -import merge from 'deepmerge' -import glob from 'fast-glob' -import readPkgUp from 'read-pkg-up' - -import { Platforms } from './platforms' -import { throwError, normalizePaths } from './utils' - -type InputTheme = { - mappers: string[] - sources: string[] - whitepaper: Record - platforms: Platforms[] - extends?: string -} - -type OutputTheme = { - mappers: string[] - // Uses nested array with paths, cuz glob not save orders with using patterns for path. - sources: string[][] - whitepaper: Record - platforms: Platforms[] -} - -function findPackageRoot(path: string): string { - const data = readPkgUp.sync({ cwd: path }) - if (data === undefined) { - throw new Error('Cannot find package root, please check exists package.json.') - } - if (data.packageJson.version !== '' && data.packageJson.name !== '') { - return dirname(data.path) - } - const prevDir = join(dirname(data.path), '..') - return findPackageRoot(prevDir) -} - -export async function loadTheme( - sources: string, - cwd: string = process.cwd(), -): Promise { - let result: OutputTheme = { mappers: [], sources: [], whitepaper: {}, platforms: ['common'] } - const theme: InputTheme = await readJSON(sources) - - if (theme.extends !== undefined) { - const [extendsPath] = await glob( - normalizePaths([resolve(cwd, 'node_modules', theme.extends), resolve(cwd, theme.extends)]), - ) - - if (extendsPath === undefined) { - throwError(`Cannot load theme: "${theme.extends}".`) - } else { - const extendsCwd = extendsPath.includes('node_modules') ? findPackageRoot(extendsPath) : cwd - const extendsTheme = await loadTheme(extendsPath, extendsCwd) - // Platforms should be defined at project theme. - delete extendsTheme.platforms - result = merge(result, extendsTheme) - } - } - - if (theme.platforms !== undefined) { - result.platforms = theme.platforms - } - - if (theme.mappers !== undefined) { - result.mappers.push(...theme.mappers.map((filePath) => join(cwd, filePath))) - } - - if (theme.whitepaper !== undefined) { - result.whitepaper = { ...result.whitepaper, ...theme.whitepaper } - } - - for (const source of theme.sources) { - // Makes array of arrays with each source for save order after glob. - result.sources.push([join(cwd, source)]) - } - - return result -} diff --git a/src/core/loadTheme.ts b/src/core/loadTheme.ts new file mode 100644 index 0000000..e69b07d --- /dev/null +++ b/src/core/loadTheme.ts @@ -0,0 +1,70 @@ +import { join, resolve, dirname } from 'path' +import { readJSON, existsSync } from 'fs-extra' +import merge from 'deepmerge' + +import { resolveFrom } from '../resolveFrom' +import { findPackageRoot } from '../findPackageRoot' +import { Platforms } from './platforms' + +export async function loadTheme( + source: string, + root: string = process.cwd(), +): Promise { + let derivedTheme: OutputTheme = { + mappers: [], + sources: [], + whitepaper: {}, + platforms: ['common'], + } + const theme: InputTheme = await readJSON(source) + + // In case with node_modules in cwd need resolve all sources from package root. + root = root.match(/node_modules/) === null ? root : findPackageRoot(root) + + if (theme.extends !== undefined) { + const parentThemePath = resolveFrom(root, theme.extends) + if (parentThemePath && existsSync(parentThemePath)) { + const parentThemeCwd = dirname(parentThemePath) + const parentTheme = await loadTheme(parentThemePath, parentThemeCwd) + // Platforms should be defined at project theme. + derivedTheme = merge(derivedTheme, { ...parentTheme, platforms: [] }) + } else { + throw new Error(`Cannot load theme: "${theme.extends}".`) + } + } + + if (theme.platforms !== undefined) { + derivedTheme.platforms = theme.platforms + } + + if (theme.mappers !== undefined) { + derivedTheme.mappers.push(...theme.mappers.map((filePath) => join(root, filePath))) + } + + if (theme.whitepaper !== undefined) { + derivedTheme.whitepaper = { ...derivedTheme.whitepaper, ...theme.whitepaper } + } + + for (const source of theme.sources) { + // Makes array of arrays with each source for save order after glob. + derivedTheme.sources.push([resolve(root, source)]) + } + + return derivedTheme +} + +type InputTheme = { + mappers: string[] + sources: string[] + whitepaper: Record + platforms: Platforms[] + extends?: string +} + +type OutputTheme = { + mappers: string[] + // Uses nested array with paths, cuz glob not save orders with using patterns for path. + sources: string[][] + whitepaper: Record + platforms: Platforms[] +} diff --git a/src/core/utils.ts b/src/core/utils.ts index d91db35..dbac7a0 100644 --- a/src/core/utils.ts +++ b/src/core/utils.ts @@ -2,10 +2,6 @@ import normalize from 'normalize-path' import { Platforms } from '../core/platforms' -export function throwError(messag: string): void { - throw new Error(messag) -} - export function getPlatformFromFilePath(filePath: string): Platforms { const matched = filePath.match(/@([\w|-]+)+\./) return matched === null ? 'common' : (matched[1] as Platforms) diff --git a/src/findPackageRoot.ts b/src/findPackageRoot.ts new file mode 100644 index 0000000..cdb0f2a --- /dev/null +++ b/src/findPackageRoot.ts @@ -0,0 +1,14 @@ +import { dirname, join } from 'path' +import readPkgUp from 'read-pkg-up' + +export function findPackageRoot(path: string): string { + const data = readPkgUp.sync({ cwd: path }) + if (data === undefined) { + throw new Error('Cannot find package root, please check exists package.json.') + } + if (data.packageJson.version !== '' && data.packageJson.name !== '') { + return dirname(data.path) + } + const prevDir = join(dirname(data.path), '..') + return findPackageRoot(prevDir) +} diff --git a/src/resolveFrom.ts b/src/resolveFrom.ts new file mode 100644 index 0000000..b92e5bc --- /dev/null +++ b/src/resolveFrom.ts @@ -0,0 +1,30 @@ +import Module from 'module' +import path from 'path' +import fs from 'fs' + +export function resolveFrom(fromDirectory: string, moduleId: string): string | undefined { + try { + fromDirectory = fs.realpathSync(fromDirectory) + } catch (error) { + if (error.code === 'ENOENT') { + fromDirectory = path.resolve(fromDirectory) + } else { + return + } + } + + const fromFile = path.join(fromDirectory, 'noop.js') + + const resolveFileName = () => + (Module as any)._resolveFilename(moduleId, { + id: fromFile, + filename: fromFile, + paths: (Module as any)._nodeModulePaths(fromDirectory), + }) + + try { + return resolveFileName() + } catch (error) { + return + } +}