Skip to content

Instantly share code, notes, and snippets.

@iiic
Created May 8, 2026 18:54
Show Gist options
  • Select an option

  • Save iiic/3711522d00e637ce6b1073edc1819405 to your computer and use it in GitHub Desktop.

Select an option

Save iiic/3711522d00e637ce6b1073edc1819405 to your computer and use it in GitHub Desktop.
testuje js soubory na příslušný content type
"use strict";
//@ts-check
export default ( async () =>
{
/** @type {Number} */
const REPORT = 1;
/** @type {Number} */
const WARNING = 2;
/** @type {Object} */
const TESTING_FILE_EXTENSIONS = {
js: WARNING,
mjs: REPORT,
};
/* @type {{supported: Array, deprecated: Array, nonStandard: Array}} */
const JS_CONTENT_TYPES = {
supported: [
'text/javascript'
],
deprecated: [
'application/javascript',
'application/ecmascript',
'text/ecmascript'
],
nonStandard: [
'application/x-ecmascript',
'application/x-javascript',
'text/javascript1.0',
'text/javascript1.1',
'text/javascript1.2',
'text/javascript1.3',
'text/javascript1.4',
'text/javascript1.5',
'text/jscript',
'text/livescript',
'text/x-ecmascript',
'text/x-javascript'
],
};
/** @returns {String|null} */
const findPathToFirstMjsFile = ( /** @type {HTMLCollectionOf<HTMLScriptElement>} */ scriptElements, /** @type {String} */ fileExtension = 'mjs' ) =>
{
for (/** @type {HTMLScriptElement} */ const scriptElement of scriptElements ) {
if ( scriptElement.src
&& ( scriptElement.src.endsWith( '.' + fileExtension )
|| scriptElement.src.includes( '.' + fileExtension + '?' )
)
) {
return scriptElement.src;
}
}
return null;
};
/** @returns {String} */
const getCleanedContentType = ( /** @type {String} */ contentType ) =>
{
/** @type {Array.<String>} */
const parts = contentType.split( ';' );
return parts[ 0 ].trimEnd();
}
/** @returns {void} */
const propagateReport = ( /** @type {{ fileExtension: string, path: string; reportingType: number; contentType: string; }} */ testingItem, /** @type {String} */ reportResult ) =>
{
/** @type {String} */
let message = '';
if ( reportResult === 'deprecated' ) {
message = 'Javascript file extension ".' + testingItem.fileExtension + '" is associated with content type "' + testingItem.contentType + '" (for example file ' + testingItem.path + ' ). This is marked as deprecated ( https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/MIME_types#textjavascript ) only proper mime type is now ' + JS_CONTENT_TYPES.supported.join( ',' ) + ' .';
} else if ( reportResult === 'nonStandard' ) {
message = 'Javascript file extension ".' + testingItem.fileExtension + '" is associated with content type "' + testingItem.contentType + '" (for example file ' + testingItem.path + ' ). This is marked as non-standard ( https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/MIME_types#textjavascript ) only proper mime type is now ' + JS_CONTENT_TYPES.supported.join( ',' ) + ' .';
} else { // error
message = 'File extension ".' + testingItem.fileExtension + '" is NOT associated with content type for javascript ' + JS_CONTENT_TYPES.supported.join( ',' ) + ' ! Some scripts will NOT work (for example file ' + testingItem.path + ')';
}
if ( testingItem.reportingType === REPORT ) {
alert( message );
} else if ( testingItem.reportingType === WARNING ) {
console.warn( message );
}
}
/** @returns {void} */
const reportResult = ( /** @type {{ fileExtension: string, path: string; reportingType: number; contentType: string; }} */ testingItem ) =>
{
/** @type {String} */
const cleanedContentType = getCleanedContentType( testingItem.contentType );
if ( JS_CONTENT_TYPES.supported.includes( cleanedContentType ) ) {
return; // good result
} else if ( JS_CONTENT_TYPES.deprecated.includes( cleanedContentType ) ) {
propagateReport( testingItem, 'deprecated' );
} else if ( JS_CONTENT_TYPES.nonStandard.includes( cleanedContentType ) ) {
propagateReport( testingItem, 'nonStandard' );
} else {
propagateReport( testingItem, 'error' );
}
};
/** @returns {Promise<Array.<Promise.<{ fileExtension: string, path: string; reportingType: number; contentType: string; }>>>} */
const prepareFetches = async ( /** @type {Array<{fileExtension: string, path: string, reportingType: number, contentType: string}>} */ testingFileExtensionsObject ) =>
{
/** @type {Array.<Promise<{fileExtension: string, path: string; reportingType: number; contentType: string; }>>} */
const promises = [];
testingFileExtensionsObject.forEach( ( /** @type {{fileExtension: string, path: string, reportingType: number, contentType: string}} */ testingItem ) =>
{
promises.push( fetch( testingItem.path, {
method: 'HEAD',
credentials: 'omit',
cache: 'no-store',
referrerPolicy: 'no-referrer',
redirect: 'follow',
mode: 'cors'
} ).then( ( /** @type {Response} */ response ) =>
{
if ( response && response.ok && response.status === 200 ) {
return response.headers.get( 'content-type' );
}
throw new Error( 'Cannot fetch :(' );
} ).then( ( /** @type {String|null} */ contentType ) =>
{
if ( contentType ) {
testingItem.contentType = contentType;
return testingItem;
}
throw new Error( 'Content type of file ' + testingItem.path + ' not found' );
} ).catch( ( /** @type {Error|Response} */ error ) =>
{
return Promise.reject( error );
} )
);
} );
return promises;
}
/** @type {Array<{fileExtension: string, path: string, reportingType: number, contentType: string}>} */
const testingFileExtensionsObject = [];
Object.entries( TESTING_FILE_EXTENSIONS ).forEach( ( [
/** @type {String} */ fileExtension,
/** @type {Number} */ reportingType
] ) =>
{
/** @type {String} */
let firstScriptSrc = '';
/** @type {HTMLCollectionOf<HTMLScriptElement>} */
let scriptElements = document.head.getElementsByTagName( 'script' );
if ( scriptElements.length ) {
firstScriptSrc = findPathToFirstMjsFile( scriptElements, fileExtension ) ?? '';
}
if ( !firstScriptSrc ) {
firstScriptSrc = findPathToFirstMjsFile( document.body.getElementsByTagName( 'script' ), fileExtension ) ?? '';
}
if ( firstScriptSrc ) {
testingFileExtensionsObject.push( {
fileExtension: fileExtension,
path: firstScriptSrc,
reportingType: reportingType,
contentType: '' // default empty string, real value will be fetched later
} );
}
} );
/** @type {Array.<Promise<{ fileExtension: string, path: string; reportingType: number; contentType: string; }>>} */
const promises = await prepareFetches( testingFileExtensionsObject );
/** @type {Array.<{ fileExtension: string, path: string; reportingType: number; contentType: string; }>} */
const testingResults = await Promise.all( promises );
testingResults.forEach( ( /** @type {{ fileExtension: string, path: string; reportingType: number; contentType: string; }} */ testingItem ) =>
{
reportResult( testingItem );
} );
} )();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment