@@ -4563,6 +4563,7 @@ ${reason}`,
45634563 }
45644564 return createFailedToResolveUrlError ( {
45654565 reason : `An error occured during specifier resolution` ,
4566+ ...detailsFromInjectionsOnOwner ( reference ) ,
45664567 ...detailsFromValueThrown ( error ) ,
45674568 } ) ;
45684569} ;
@@ -4624,6 +4625,7 @@ ${reason}`,
46244625 return createFailedToFetchUrlContentError ( {
46254626 code : "NOT_FOUND" ,
46264627 reason : "no entry on filesystem" ,
4628+ ...detailsFromInjectionsOnOwner ( urlInfo . firstReference ) ,
46274629 } ) ;
46284630 }
46294631 }
@@ -4856,6 +4858,32 @@ const getFirstReferenceInProject = (reference) => {
48564858 return getFirstReferenceInProject ( firstReference ) ;
48574859} ;
48584860
4861+ // An url written by an injection cannot be resolved: the placeholder is still there
4862+ // when references are analyzed. Rather than guessing what a placeholder looks like
4863+ // (the key is free-form), tell the file it comes from: injections are configured for it.
4864+ const detailsFromInjectionsOnOwner = ( reference ) => {
4865+ if ( ! reference ) {
4866+ return { } ;
4867+ }
4868+ const ownerUrlInfo = reference . ownerUrlInfo ;
4869+ if ( ownerUrlInfo . type !== "html" ) {
4870+ // "jsenv-ignore" is an html attribute
4871+ return { } ;
4872+ }
4873+ const { hasInjections } = ownerUrlInfo . context ;
4874+ if ( ! hasInjections || ! hasInjections ( ownerUrlInfo . url ) ) {
4875+ return { } ;
4876+ }
4877+ const { node, attributeName } = reference . astInfo || { } ;
4878+ if ( ! node || ! attributeName ) {
4879+ return { } ;
4880+ }
4881+ return {
4882+ suggestion : `injections are configured for this file; when "${ reference . specifier } " is meant to be written by one of them, add "jsenv-ignore" so jsenv leaves that url alone:
4883+ <${ node . nodeName } jsenv-ignore ${ attributeName } ="${ reference . specifier } " />` ,
4884+ } ;
4885+ } ;
4886+
48594887const detailsFromPluginController = ( jsenvPluginsController ) => {
48604888 const currentPlugin = jsenvPluginsController . getCurrentPlugin ( ) ;
48614889 if ( ! currentPlugin ) {
@@ -5145,7 +5173,14 @@ const injectGlobals = (content, globals, urlInfo) => {
51455173 if ( urlInfo . type === "js_classic" || urlInfo . type === "js_module" ) {
51465174 return globalsInjectorOnJs ( content , globals , urlInfo ) ;
51475175 }
5148- throw new Error ( `cannot inject globals into "${ urlInfo . type } "` ) ;
5176+ throw new Error (
5177+ createDetailedMessage ( `cannot inject globals into "${ urlInfo . type } "` , {
5178+ file : urlInfo . url ,
5179+ ...( urlInfo . isInline
5180+ ? { "inline content of" : urlInfo . inlineUrlSite . url }
5181+ : { } ) ,
5182+ } ) ,
5183+ ) ;
51495184} ;
51505185const globalInjectorOnHtml = ( content , globals , urlInfo ) => {
51515186 // ideally we would inject an importmap but browser support is too low
@@ -5211,11 +5246,20 @@ const jsenvPluginInjections = (rawAssociations) => {
52115246 { injectionsGetter : rawAssociations } ,
52125247 context . rootDirectoryUrl ,
52135248 ) ;
5214- const findInjectionsGetter = ( urlInfo ) => {
5249+ const findInjectionsGetterForUrl = ( url ) => {
52155250 const { injectionsGetter } = URL_META . applyAssociations ( {
5216- url : asUrlWithoutSearch ( urlInfo . url ) ,
5251+ url : asUrlWithoutSearch ( url ) ,
52175252 associations : resolvedAssociations ,
52185253 } ) ;
5254+ return injectionsGetter ;
5255+ } ;
5256+ // an url written by an injection cannot be resolved during reference analysis;
5257+ // errors use this to tell the file holds injections and suggest "jsenv-ignore"
5258+ context . hasInjections = ( url ) => {
5259+ return Boolean ( findInjectionsGetterForUrl ( url ) ) ;
5260+ } ;
5261+ const findInjectionsGetter = ( urlInfo ) => {
5262+ const injectionsGetter = findInjectionsGetterForUrl ( urlInfo . url ) ;
52195263 if ( injectionsGetter ) {
52205264 return { injectionsGetter, isInherited : false } ;
52215265 }
@@ -5247,9 +5291,7 @@ const jsenvPluginInjections = (rawAssociations) => {
52475291 if ( ! injections || ! isInherited ) {
52485292 return injections ;
52495293 }
5250- // the file holds several inline contents; a placeholder configured for the file
5251- // is expected in one of them, not in each
5252- return asOptionalInjections ( injections ) ;
5294+ return asInheritedInjections ( injections ) ;
52535295 } ;
52545296 }
52555297 } ,
@@ -5276,12 +5318,24 @@ const jsenvPluginInjections = (rawAssociations) => {
52765318 } ;
52775319} ;
52785320
5279- const asOptionalInjections = ( injections ) => {
5280- const optionalInjections = { } ;
5321+ // What a file inlines (a <script> or a <style> inside html) is authored in that file
5322+ // and inherits its injections, with two adjustments:
5323+ // - a global belongs to the file itself, injecting it into each inline content would
5324+ // repeat it and reach types that cannot receive globals (css)
5325+ // - a placeholder configured for the file is expected in one of its inline contents,
5326+ // not in each, so a missing one is not worth a warning
5327+ const asInheritedInjections = ( injections ) => {
5328+ const inheritedInjections = { } ;
52815329 for ( const key of Object . keys ( injections ) ) {
5282- optionalInjections [ key ] = INJECTIONS . optional ( injections [ key ] ) ;
5330+ const value = injections [ key ] ;
5331+ if ( isPlaceholderInjection ( value ) ) {
5332+ inheritedInjections [ key ] = INJECTIONS . optional ( value ) ;
5333+ }
5334+ }
5335+ if ( Object . keys ( inheritedInjections ) . length === 0 ) {
5336+ return null ;
52835337 }
5284- return optionalInjections ;
5338+ return inheritedInjections ;
52855339} ;
52865340
52875341const jsenvPluginInliningAsDataUrl = ( ) => {
0 commit comments