|
|
|
import { cyan, red, yellow } from 'kleur/colors'; |
|
import debug from 'debug'; |
|
import { VERSION } from 'svelte/compiler'; |
|
|
|
|
|
const levels = ['debug', 'info', 'warn', 'error', 'silent']; |
|
const prefix = 'vite-plugin-svelte'; |
|
|
|
const loggers = { |
|
debug: { |
|
log: debug(`${prefix}`), |
|
enabled: false, |
|
isDebug: true |
|
}, |
|
info: { |
|
color: cyan, |
|
log: console.log, |
|
enabled: true |
|
}, |
|
warn: { |
|
color: yellow, |
|
log: console.warn, |
|
enabled: true |
|
}, |
|
error: { |
|
color: red, |
|
log: console.error, |
|
enabled: true |
|
}, |
|
silent: { |
|
enabled: false |
|
} |
|
}; |
|
|
|
|
|
let _level = 'info'; |
|
|
|
|
|
|
|
|
|
function setLevel(level) { |
|
if (level === _level) { |
|
return; |
|
} |
|
const levelIndex = levels.indexOf(level); |
|
if (levelIndex > -1) { |
|
_level = level; |
|
for (let i = 0; i < levels.length; i++) { |
|
loggers[levels[i]].enabled = i >= levelIndex; |
|
} |
|
} else { |
|
_log(loggers.error, `invalid log level: ${level} `); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function _log(logger, message, payload, namespace) { |
|
if (!logger.enabled) { |
|
return; |
|
} |
|
if (logger.isDebug) { |
|
let log = logger.log; |
|
if (namespace) { |
|
if (!isDebugNamespaceEnabled(namespace)) { |
|
return; |
|
} |
|
log = logger.log.extend(namespace); |
|
} |
|
payload !== undefined ? log(message, payload) : log(message); |
|
} else { |
|
logger.log( |
|
logger.color( |
|
`${new Date().toLocaleTimeString()} [${prefix}${ |
|
namespace ? `:${namespace}` : '' |
|
}] ${message}` |
|
) |
|
); |
|
if (payload) { |
|
logger.log(payload); |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
function createLogger(level) { |
|
const logger = loggers[level]; |
|
const logFn = (_log.bind(null, logger)); |
|
|
|
const logged = new Set(); |
|
|
|
const once = function (message, payload, namespace) { |
|
if (!logger.enabled || logged.has(message)) { |
|
return; |
|
} |
|
logged.add(message); |
|
logFn.apply(null, [message, payload, namespace]); |
|
}; |
|
Object.defineProperty(logFn, 'enabled', { |
|
get() { |
|
return logger.enabled; |
|
} |
|
}); |
|
Object.defineProperty(logFn, 'once', { |
|
get() { |
|
return once; |
|
} |
|
}); |
|
return logFn; |
|
} |
|
|
|
export const log = { |
|
debug: createLogger('debug'), |
|
info: createLogger('info'), |
|
warn: createLogger('warn'), |
|
error: createLogger('error'), |
|
setLevel |
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
export function logCompilerWarnings(svelteRequest, warnings, options) { |
|
const { emitCss, onwarn, isBuild } = options; |
|
const sendViaWS = !isBuild && options.experimental?.sendWarningsToBrowser; |
|
let warn = isBuild ? warnBuild : warnDev; |
|
|
|
const handledByDefaultWarn = []; |
|
const notIgnored = warnings?.filter((w) => !ignoreCompilerWarning(w, isBuild, emitCss)); |
|
const extra = buildExtraWarnings(warnings, isBuild); |
|
const allWarnings = [...notIgnored, ...extra]; |
|
if (sendViaWS) { |
|
const _warn = warn; |
|
|
|
warn = (w) => { |
|
handledByDefaultWarn.push(w); |
|
_warn(w); |
|
}; |
|
} |
|
allWarnings.forEach((warning) => { |
|
if (onwarn) { |
|
onwarn(warning, warn); |
|
} else { |
|
warn(warning); |
|
} |
|
}); |
|
if (sendViaWS) { |
|
|
|
const message = { |
|
id: svelteRequest.id, |
|
filename: svelteRequest.filename, |
|
normalizedFilename: svelteRequest.normalizedFilename, |
|
timestamp: svelteRequest.timestamp, |
|
warnings: handledByDefaultWarn, |
|
allWarnings, |
|
rawWarnings: warnings |
|
}; |
|
log.debug(`sending svelte:warnings message for ${svelteRequest.normalizedFilename}`); |
|
options.server?.ws?.send('svelte:warnings', message); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function ignoreCompilerWarning(warning, isBuild, emitCss) { |
|
return ( |
|
(!emitCss && warning.code === 'css-unused-selector') || |
|
(!isBuild && isNoScopableElementWarning(warning)) |
|
); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
function isNoScopableElementWarning(warning) { |
|
|
|
return warning.code === 'css-unused-selector' && warning.message.includes('"*"'); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function buildExtraWarnings(warnings, isBuild) { |
|
const extraWarnings = []; |
|
if (!isBuild) { |
|
const noScopableElementWarnings = warnings.filter((w) => isNoScopableElementWarning(w)); |
|
if (noScopableElementWarnings.length > 0) { |
|
|
|
const noScopableElementWarning = |
|
noScopableElementWarnings[noScopableElementWarnings.length - 1]; |
|
extraWarnings.push({ |
|
...noScopableElementWarning, |
|
code: 'vite-plugin-svelte-css-no-scopable-elements', |
|
message: |
|
"No scopable elements found in template. If you're using global styles in the style tag, you should move it into an external stylesheet file and import it in JS. See https://github.com/sveltejs/vite-plugin-svelte/blob/main/docs/faq.md#where-should-i-put-my-global-styles." |
|
}); |
|
} |
|
} |
|
return extraWarnings; |
|
} |
|
|
|
|
|
|
|
|
|
function warnDev(w) { |
|
log.info.enabled && log.info(buildExtendedLogMessage(w)); |
|
} |
|
|
|
|
|
|
|
|
|
function warnBuild(w) { |
|
log.warn.enabled && log.warn(buildExtendedLogMessage(w), w.frame); |
|
} |
|
|
|
|
|
|
|
|
|
export function buildExtendedLogMessage(w) { |
|
const parts = []; |
|
if (w.filename) { |
|
parts.push(w.filename); |
|
} |
|
if (w.start) { |
|
parts.push(':', w.start.line, ':', w.start.column); |
|
} |
|
if (w.message) { |
|
if (parts.length > 0) { |
|
parts.push(' '); |
|
} |
|
parts.push(w.message); |
|
} |
|
return parts.join(''); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
export function isDebugNamespaceEnabled(namespace) { |
|
return debug.enabled(`${prefix}:${namespace}`); |
|
} |
|
|
|
export function logSvelte5Warning() { |
|
const notice = `You are using Svelte ${VERSION}. Svelte 5 support is experimental, breaking changes can occur in any release until this notice is removed.`; |
|
const wip = ['svelte-inspector is disabled until dev mode implements node to code mapping']; |
|
log.warn(`${notice}\nwork in progress:\n - ${wip.join('\n - ')}\n`); |
|
} |
|
|