@@ -2,76 +2,68 @@ import { isAbsolute, join, resolve as resolvePath } from 'node:path';
22import { pathToFileURL } from 'node:url' ;
33import { moduleResolve } from 'import-meta-resolve' ;
44import type { Options as PrettierOptions } from 'prettier' ;
5- import type { ResolvedFmtConfig } from './types.ts' ;
5+ import type { FmtPluginSpecifier } from './types.ts' ;
66
77type FmtPlugin = NonNullable < PrettierOptions [ 'plugins' ] > [ number ] ;
8+ type FmtPluginResolver = ( options : PrettierOptions ) => PrettierOptions ;
89
910const resolveModuleUrl = ( specifier : string , parentUrl : URL ) : string =>
1011 moduleResolve ( specifier , parentUrl ) . href ;
1112
12- const resolvePlugin = ( plugin : FmtPlugin , rootPath : string , parentUrl : URL ) : FmtPlugin => {
13- if ( plugin instanceof URL ) {
14- return resolveModuleUrl ( plugin . href , parentUrl ) ;
15- }
16- if ( typeof plugin !== 'string' ) {
17- return plugin ;
18- }
19- if ( isAbsolute ( plugin ) ) {
20- return resolveModuleUrl ( pathToFileURL ( plugin ) . href , parentUrl ) ;
21- }
22- if ( URL . canParse ( plugin ) ) {
23- return resolveModuleUrl ( plugin , parentUrl ) ;
24- }
13+ const isFmtPluginSpecifier = ( plugin : FmtPlugin ) : plugin is FmtPluginSpecifier =>
14+ typeof plugin === 'string' || plugin instanceof URL ;
2515
26- try {
27- return resolveModuleUrl ( pathToFileURL ( resolvePath ( rootPath , plugin ) ) . href , parentUrl ) ;
28- } catch {
29- return resolveModuleUrl ( plugin , parentUrl ) ;
30- }
31- } ;
32-
33- const resolveOptionsPlugins = (
34- options : PrettierOptions ,
35- rootPath : string ,
36- parentUrl : URL ,
37- ) : PrettierOptions => {
38- const { plugins } = options ;
39- if ( ! plugins ?. some ( ( plugin ) => typeof plugin === 'string' || plugin instanceof URL ) ) {
40- return options ;
41- }
42-
43- const resolvedPlugins = plugins . map ( ( plugin ) => resolvePlugin ( plugin , rootPath , parentUrl ) ) ;
44-
45- return resolvedPlugins . every ( ( plugin , index ) => plugin === plugins [ index ] )
46- ? options
47- : { ...options , plugins : resolvedPlugins } ;
48- } ;
49-
50- /** Resolves plugin specifiers from the Rstack config root. */
51- const resolveFmtConfigPlugins = ( config : ResolvedFmtConfig ) : ResolvedFmtConfig => {
52- const { rootPath } = config ;
16+ /** Creates a project-root resolver for plugins in final per-file options. */
17+ const createFmtPluginResolver = ( rootPath : string ) : FmtPluginResolver => {
5318 const parentUrl = pathToFileURL ( join ( rootPath , 'index.js' ) ) ;
54- const baseOptions = resolveOptionsPlugins ( config . baseOptions , rootPath , parentUrl ) ;
55- let overrides = config . overrides ;
19+ const cache = new Map < string , string > ( ) ;
5620
57- for ( let index = 0 ; index < overrides . length ; index ++ ) {
58- const override = overrides [ index ] ;
59- if ( ! override . options ) {
60- continue ;
21+ const resolvePlugin = ( plugin : FmtPluginSpecifier ) : string => {
22+ const specifier = plugin instanceof URL ? plugin . href : plugin ;
23+ const cached = cache . get ( specifier ) ;
24+ if ( cached !== undefined ) {
25+ return cached ;
6126 }
6227
63- const options = resolveOptionsPlugins ( override . options , rootPath , parentUrl ) ;
64- if ( options !== override . options ) {
65- if ( overrides === config . overrides ) {
66- overrides = [ ...overrides ] ;
28+ let resolved : string ;
29+ if ( isAbsolute ( specifier ) ) {
30+ resolved = resolveModuleUrl ( pathToFileURL ( specifier ) . href , parentUrl ) ;
31+ } else if ( URL . canParse ( specifier ) ) {
32+ resolved = resolveModuleUrl ( specifier , parentUrl ) ;
33+ } else {
34+ try {
35+ resolved = resolveModuleUrl (
36+ pathToFileURL ( resolvePath ( rootPath , specifier ) ) . href ,
37+ parentUrl ,
38+ ) ;
39+ } catch {
40+ resolved = resolveModuleUrl ( specifier , parentUrl ) ;
6741 }
68- overrides [ index ] = { ...override , options } ;
6942 }
70- }
7143
72- return baseOptions === config . baseOptions && overrides === config . overrides
73- ? config
74- : { ...config , baseOptions, overrides } ;
44+ cache . set ( specifier , resolved ) ;
45+ return resolved ;
46+ } ;
47+
48+ return ( options ) => {
49+ const { plugins } = options ;
50+ if ( ! plugins ?. length ) {
51+ return options ;
52+ }
53+ if ( ! plugins . every ( isFmtPluginSpecifier ) ) {
54+ // Imported plugin objects are not planned for support.
55+ throw new TypeError (
56+ 'Prettier plugin objects are not supported. Use a package name, path, or URL instead.' ,
57+ ) ;
58+ }
59+
60+ const resolvedPlugins = plugins . map ( resolvePlugin ) ;
61+
62+ return resolvedPlugins . every ( ( plugin , index ) => plugin === plugins [ index ] )
63+ ? options
64+ : { ...options , plugins : resolvedPlugins } ;
65+ } ;
7566} ;
7667
77- export { resolveFmtConfigPlugins } ;
68+ export { createFmtPluginResolver } ;
69+ export type { FmtPluginResolver } ;
0 commit comments