Skip to content

feat: typescript version#209

Open
wayfarer3130 wants to merge 1 commit into
pimterry:v2from
wayfarer3130:feat/typescript-version
Open

feat: typescript version#209
wayfarer3130 wants to merge 1 commit into
pimterry:v2from
wayfarer3130:feat/typescript-version

Conversation

@wayfarer3130
Copy link
Copy Markdown

@wayfarer3130 wayfarer3130 commented Jan 21, 2026

This is a potential version for typescript - the api is compatible, but I haven't used much extra in terms of the typescript versioning of any ability to over-ride a class instance.

Note the lib/loglevel.js javascript version is GENERATED now from src/loglevel.ts. That is one of the things to discuss about what level of backwards compatibility this should have and how much new features should be taken advantage of.
Please provide comments about overall goals for a v2 - we can discuss the nitpicky details once we have the high level agreement on the general pattern.

@wayfarer3130
Copy link
Copy Markdown
Author

@pimterry - this is a potential version converted to typescript, mostly as a stalking horse to talk about to see if we can figure out the right changes for a v2 release.

Copy link
Copy Markdown

@Ryuno-Ki Ryuno-Ki left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I highlighted some areas where the spaces appear to be out of order.
Replacing vars can be a chore taken care of in another PR.

Comment thread .github/workflows/ci.yml Outdated
pull_request: {}
push:
branches:
- main
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing this would make it run for pushes to any branch.

Comment thread .gitignore
*.gz
*.tgz
*.code-workspace
package-lock.json
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file should be tracked in version control.

Comment thread .gitignore
*.tgz
*.code-workspace
package-lock.json
bun.lock
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file should be tracked in version control.

Comment thread lib/loglevel.js Outdated
var isIE = (typeof window !== undefinedType) && (typeof window.navigator !== undefinedType) && (
/Trident\/|MSIE /.test(window.navigator.userAgent)
);
const noop = function() {};
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want to be consistent:

Suggested change
const noop = function() {};
const noop = function () {};

Comment thread lib/loglevel.js Outdated
function realMethod(methodName) {
if (methodName === 'debug') {
methodName = 'log';
function defaultMethodFactory(methodName, _level, _loggerName) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
function defaultMethodFactory(methodName, _level, _loggerName) {
function defaultMethodFactory (methodName, _level, _loggerName) {

Comment thread lib/loglevel.js Outdated
* in sync with the actual logging methods that we have installed (the
* parent could change levels but we might not have rebuilt the loggers
* in this child yet).
* @type {number}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't this conflict with the initialisation?

Suggested change
* @type {number}
* @type {number|null}

Comment thread lib/loglevel.js Outdated

self.setDefaultLevel = function (level) {
defaultLevel = normalizeLevel(level);
if (getPersistedLevel()===undefined) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick:

Suggested change
if (getPersistedLevel()===undefined) {
if (getPersistedLevel() === undefined) {

Comment thread lib/loglevel.js Outdated
self.rebuild = function () {
inheritedLevel = null;
const result = replaceLoggingMethods.call(self);
if( self.methodFactory && self.methodFactory===(factory || defaultMethodFactory)) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if( self.methodFactory && self.methodFactory===(factory || defaultMethodFactory)) {
if (self.methodFactory && self.methodFactory === (factory || defaultMethodFactory)) {

window.console = mockConsole();
log.trace("trace");
it("silent method calls are allowed", function() {
var result = log.setLevel(log.levels.SILENT);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe worth to replace the vars here, too.

});

it("child loggers inherit parent", function(log) {
log.setLevel(0);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm. This appears to be an implementation detail.

@wayfarer3130
Copy link
Copy Markdown
Author

I highlighted some areas where the spaces appear to be out of order. Replacing vars can be a chore taken care of in another PR.

The lib/loglevel.js file is now a generated file for compatibility - if v2 doesn't need that level of compatibility, then the ts can be the primary version and we can avoid generating a lib/loglevel.js Alterantively, we could leave lib/loglevel.ts as the primary typescript version, and add a compatible version in dist, and change the exports in package.json.

@Ryuno-Ki
Copy link
Copy Markdown

Ah, so that's what you meant with TS! I was confused as I haven't seen any changes to TS files.

@pimterry
Copy link
Copy Markdown
Owner

I'm very open to migrating to TS! Fully agree that would be good. It should happen on the V2 branch though, we're not migrating v1 to TS - it's frozen and there's risk for little benefit. This PR currently targets the main branch and seems to include all the other V2 changes here as well, which makes it hard to review. Can you update it to target v2, and only include the TS conversion?

@Mr0grog
Copy link
Copy Markdown
Contributor

Mr0grog commented Jan 23, 2026

@wayfarer3130 sorry I’ve been so absent when I thought I’d have time to work on this. Glad someone’s taking it on.

Did you maybe push the wrong commit to your PR branch? This PR appears to be the exact same as the current v2 branch in this repo (same hash, so nothing’s been inserted into the history), and there’s no src/loglevel.ts file like you’re describing.

@wayfarer3130
Copy link
Copy Markdown
Author

@wayfarer3130 sorry I’ve been so absent when I thought I’d have time to work on this. Glad someone’s taking it on.

Did you maybe push the wrong commit to your PR branch? This PR appears to be the exact same as the current v2 branch in this repo (same hash, so nothing’s been inserted into the history), and there’s no src/loglevel.ts file like you’re describing.

I must have pushed it wrong somehow as it doesn't seem to have the changes I had locally. I will push that on Monday when I'm back on the computer I was working on.
Even when I do push it, I'm mostly thinking it is a discussion point, and is extremely unlikely to be what we use for v2, just that I wanted to get people talking about what they want in v2.

@wayfarer3130 wayfarer3130 changed the base branch from main to v2 February 2, 2026 16:03
Comment thread lib/loglevel.js
// Build the best logging method possible for this env
// Wherever possible we want to bind, not wrap, to preserve stack traces
function defaultMethodFactory(methodName, _level, _loggerName) {
/*! loglevel - https://github.com/pimterry/loglevel - licensed MIT */
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure whether the comment can be changed into this form legally. Would it be possible to preserve the previous version?

Copy link
Copy Markdown

@Ryuno-Ki Ryuno-Ki left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like it's on a good path. I feel like the typings could be more rigour.

Comment thread src/loglevel.ts

function defaultMethodFactory(methodName: LogMethodName): LoggingMethod {
if (typeof console !== undefinedType) {
const anyConsole = console as any;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we prefer unknown over any?

Comment thread src/loglevel.ts
/*jshint validthis:true */
const level = this.getLevel();

for (let i = 0; i < logMethods.length; i++) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My hunch is that a for…of loop is perhaps the right way?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or perhaps even a logMethods.forEach((method, index) => {…})?

Comment thread src/loglevel.ts

for (let i = 0; i < logMethods.length; i++) {
const methodName = logMethods[i];
this[methodName] = i < level ? noop : this.methodFactory(methodName, level, this.name);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The i should maybe be casted here. It feels kind of confusing that level is an integer in this line. (It makes me think which I interpret as a smell)

Comment thread src/loglevel.ts
}
}

function Logger(this: any, name?: LoggerName, factory?: MethodFactory, parent?: any) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might benefit from a return type.

Comment thread src/loglevel.ts
function Logger(this: any, name?: LoggerName, factory?: MethodFactory, parent?: any) {
const self = this;

// Private instance variables:
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

JavaScript has private fields by now.

Comment thread src/loglevel.ts

const loggers: any = {};

function persistLevelIfPossible(levelNum: number): void {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might make sense to define a type for Indices like this:

// Source - https://stackoverflow.com/a/63904714
// Posted by bela53
// Retrieved 2026-02-04, License - CC BY-SA 4.0

type Indices<T extends readonly any[]> = Exclude<Partial<T>["length"], T["length"]>

Would then be

Suggested change
function persistLevelIfPossible(levelNum: number): void {
function persistLevelIfPossible(levelNum: Indices<typeof LogMethodName[]>): void {

if I read it correctly.

Comment thread src/loglevel.ts
if (typeof window === undefinedType || !storageKey) return;

try {
(window as any).localStorage[storageKey] = levelName;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is casting window really necessary here?
You check for typeof window above. Maybe there should be a general check for browser context before handling finer levels.

Comment thread src/loglevel.ts

function getPersistedLevel(): string | void {
let storedLevel: any;
if (typeof window === undefinedType || !storageKey) return;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This starts to ask for a refactoring that handles the question of browser vs. server context generally.

Comment thread package.json
"test-types": "tsc --noEmit ./test/type-test.ts && ts-node ./test/type-test.ts",
"dist": "grunt dist",
"dist-build": "grunt dist-build",
"test-types": "tsc --noEmit ./test/type-test.ts && ts-node --compilerOptions \"{\\\"module\\\":\\\"CommonJS\\\"}\" ./test/type-test.ts",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really have to have this level of escaping?

Suggested change
"test-types": "tsc --noEmit ./test/type-test.ts && ts-node --compilerOptions \"{\\\"module\\\":\\\"CommonJS\\\"}\" ./test/type-test.ts",
"test-types": "tsc --noEmit ./test/type-test.ts && ts-node --compilerOptions '{\"module\":\"CommonJS\"}' ./test/type-test.ts",

feels more appropriate to me.

Comment thread rollup.config.mjs
import typescript from "@rollup/plugin-typescript";
import terser from "@rollup/plugin-terser";

const banner = `/*! loglevel - https://github.com/pimterry/loglevel - licensed MIT */`;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my comment above on the shape of the banner.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants