@@ -54,7 +54,63 @@ const optionalDependencies = {
5454 minifySvg : [ 'svgo' ]
5555} satisfies Partial < Record < keyof HtmlnanoOptions , string [ ] > > ;
5656
57- const interop = < T > ( imported : Promise < { default : T } > ) : Promise < T > => imported . then ( mod => mod . default ) ;
57+ /**
58+ * And the old mixing named export and default export again.
59+ *
60+ * TL; DR: our bundler has bundled our mixed default/named export module into a "exports" object,
61+ * and when dynamically importing a CommonJS module using "import" instead of "require", Node.js wraps
62+ * another layer of default around the "exports" object.
63+ *
64+ * The longer version:
65+ *
66+ * The bundler we are using outputs:
67+ *
68+ * ESM: export { [named], xxx as default }
69+ * CJS: exports.default = xxx; exports.[named] = ...; exports.__esModule = true;
70+ *
71+ * With ESM, the Module object looks like this:
72+ *
73+ * ```js
74+ * Module {
75+ * default: xxx,
76+ * [named]: ...,
77+ * }
78+ * ```
79+ *
80+ * With CJS, Node.js handles dynamic import differently. Node.js doesn't respect `__esModule`,
81+ * and will wrongly treat a CommonJS module as ESM, i.e. assign the "exports" object on its
82+ * own "default" on the "Module" object.
83+ *
84+ * Now we have:
85+ *
86+ * ```js
87+ * Module {
88+ * // this is actually the "exports" inside among "exports.__esModule", "exports.[named]", and "exports.default"
89+ * default: {
90+ * __esModule: true,
91+ * // This is the actual "exports.default"
92+ * default: xxx
93+ * }
94+ * }
95+ * ```
96+ */
97+ const interop = < T > ( imported : Promise < object > ) : Promise < HtmlnanoModule < T > > => imported . then ( ( mod ) => {
98+ let htmlnanoModule ;
99+ while ( 'default' in mod ) {
100+ htmlnanoModule = mod ;
101+ mod = mod . default as object ;
102+ // If we find any htmlnano module hook methods, we know this object is a htmlnano module, return directly
103+ if ( 'onAttrs' in mod || 'onContent' in mod || 'onNode' in mod ) {
104+ return mod as HtmlnanoModule < T > ;
105+ }
106+ }
107+
108+ if ( htmlnanoModule && typeof htmlnanoModule . default === 'function' ) {
109+ return htmlnanoModule as HtmlnanoModule < T > ;
110+ }
111+
112+ throw new TypeError ( 'The imported module is not a valid htmlnano module' ) ;
113+ } ) ;
58114
59115const modules = {
60116 collapseAttributeWhitespace : ( ) => interop ( import ( './_modules/collapseAttributeWhitespace' ) ) ,
@@ -63,23 +119,23 @@ const modules = {
63119 custom : ( ) => interop ( import ( './_modules/custom' ) ) ,
64120 deduplicateAttributeValues : ( ) => interop ( import ( './_modules/deduplicateAttributeValues' ) ) ,
65121 // example: () => import('./_modules/example.mjs'),
66- mergeScripts : ( ) => interop ( import ( './_modules/mergeScripts.js ' ) ) ,
67- mergeStyles : ( ) => interop ( import ( './_modules/mergeStyles.js ' ) ) ,
68- minifyConditionalComments : ( ) => interop ( import ( './_modules/minifyConditionalComments.js ' ) ) ,
69- minifyCss : ( ) => interop ( import ( './_modules/minifyCss.js ' ) ) ,
70- minifyJs : ( ) => interop ( import ( './_modules/minifyJs.js ' ) ) ,
71- minifyJson : ( ) => interop ( import ( './_modules/minifyJson.js ' ) ) ,
72- minifySvg : ( ) => interop ( import ( './_modules/minifySvg.js ' ) ) ,
73- minifyUrls : ( ) => interop ( import ( './_modules/minifyUrls.js ' ) ) ,
122+ mergeScripts : ( ) => interop ( import ( './_modules/mergeScripts' ) ) ,
123+ mergeStyles : ( ) => interop ( import ( './_modules/mergeStyles' ) ) ,
124+ minifyConditionalComments : ( ) => interop ( import ( './_modules/minifyConditionalComments' ) ) ,
125+ minifyCss : ( ) => interop ( import ( './_modules/minifyCss' ) ) ,
126+ minifyJs : ( ) => interop ( import ( './_modules/minifyJs' ) ) ,
127+ minifyJson : ( ) => interop ( import ( './_modules/minifyJson' ) ) ,
128+ minifySvg : ( ) => interop ( import ( './_modules/minifySvg' ) ) ,
129+ minifyUrls : ( ) => interop ( import ( './_modules/minifyUrls' ) ) ,
74130 normalizeAttributeValues : ( ) => interop ( import ( './_modules/normalizeAttributeValues' ) ) ,
75- removeAttributeQuotes : ( ) => interop ( import ( './_modules/removeAttributeQuotes.js ' ) ) ,
76- removeComments : ( ) => interop ( import ( './_modules/removeComments.js ' ) ) ,
77- removeEmptyAttributes : ( ) => interop ( import ( './_modules/removeEmptyAttributes.js ' ) ) ,
78- removeOptionalTags : ( ) => interop ( import ( './_modules/removeOptionalTags.js ' ) ) ,
79- removeRedundantAttributes : ( ) => interop ( import ( './_modules/removeRedundantAttributes.js ' ) ) ,
80- removeUnusedCss : ( ) => interop ( import ( './_modules/removeUnusedCss.js ' ) ) ,
81- sortAttributes : ( ) => interop ( import ( './_modules/sortAttributes.js ' ) ) ,
82- sortAttributesWithLists : ( ) => interop ( import ( './_modules/sortAttributesWithLists.js ' ) )
131+ removeAttributeQuotes : ( ) => interop ( import ( './_modules/removeAttributeQuotes' ) ) ,
132+ removeComments : ( ) => interop ( import ( './_modules/removeComments' ) ) ,
133+ removeEmptyAttributes : ( ) => interop ( import ( './_modules/removeEmptyAttributes' ) ) ,
134+ removeOptionalTags : ( ) => interop ( import ( './_modules/removeOptionalTags' ) ) ,
135+ removeRedundantAttributes : ( ) => interop ( import ( './_modules/removeRedundantAttributes' ) ) ,
136+ removeUnusedCss : ( ) => interop ( import ( './_modules/removeUnusedCss' ) ) ,
137+ sortAttributes : ( ) => interop ( import ( './_modules/sortAttributes' ) ) ,
138+ sortAttributesWithLists : ( ) => interop ( import ( './_modules/sortAttributesWithLists' ) )
83139} satisfies Record < string , ( ) => Promise < HtmlnanoModule < any > > > ;
84140
85141const htmlnano = Object . assign ( function htmlnano ( optionsRun : HtmlnanoOptions = { } , presetRun ?: HtmlnanoPreset ) {
0 commit comments