obsohtml 1.9.9 → 1.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,6 +6,8 @@ updates:
6
6
  directory: "/"
7
7
  schedule:
8
8
  interval: "monthly"
9
+ cooldown:
10
+ default-days: 7
9
11
  - package-ecosystem: "github-actions"
10
12
  directory: "/"
11
13
  schedule:
package/CHANGELOG.md ADDED
@@ -0,0 +1,12 @@
1
+ # Changelog
2
+
3
+ As of version 1.10.0, all notable changes to ObsoHTML are documented in this file, which is (mostly) AI-generated and (always) human-edited. Dependency updates may or may not be called out specifically.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and the project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [1.10.0] - 2026-04-11
8
+
9
+ ### Added
10
+
11
+ - Added programmatic API: `checkMarkup(html)` returns `{ elements, attributes }` arrays for use in other tools; `obsoleteElements` and `obsoleteAttributes` are now exported directly
12
+ - Added TypeScript declaration file (`src/index.d.ts`)
package/README.md CHANGED
@@ -2,13 +2,13 @@
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/obsohtml.svg)](https://www.npmjs.com/package/obsohtml) [![Build status](https://github.com/j9t/obsohtml/workflows/Tests/badge.svg)](https://github.com/j9t/obsohtml/actions) [![Socket](https://badge.socket.dev/npm/package/obsohtml)](https://socket.dev/npm/package/obsohtml)
4
4
 
5
- ObsoHTML is a Node.js script designed to scan HTML, PHP, Nunjucks, Twig, JavaScript, and TypeScript files for obsolete and proprietary HTML elements and attributes. It helps you identify and update deprecated HTML code to be sure to use web standards.
5
+ ObsoHTML is a Node.js tool designed to scan HTML, PHP, Nunjucks, Twig, JavaScript, and TypeScript files for obsolete and proprietary HTML elements and attributes. It helps you identify and update deprecated HTML code to be sure to use web standards.
6
6
 
7
7
  ObsoHTML has inherent limitations and may not find all obsolete elements and attributes. If you run into a problem, please [file an issue](https://github.com/j9t/obsohtml/issues).
8
8
 
9
9
  ## Usage
10
10
 
11
- ### 1. As a Node Module
11
+ ### 1. Via CLI
12
12
 
13
13
  #### Installation
14
14
 
@@ -18,11 +18,13 @@ npm i obsohtml
18
18
 
19
19
  (To install ObsoHTML globally, use the `-g` flag, as in `npm i -g obsohtml`.)
20
20
 
21
+ To use ObsoHTML without installing, run it via `npx obsohtml`. To use it as a standalone script, [download or fork the source repository](https://github.com/j9t/obsohtml) and run `node bin/obsohtml.js` directly—the same options apply.
22
+
21
23
  #### Execution
22
24
 
23
- The script accepts a folder or file path as a command line option, which can be specified in both short form (`-f`) and long form (`--folder`). The path can be either absolute or relative.
25
+ ObsoHTML accepts a folder or file path as a command line option, which can be specified in both short form (`-f`) and long form (`--folder`). The path can be either absolute or relative.
24
26
 
25
- The script can be run in “verbose” mode by appending `-v` or `--verbose` to the command. This will show information about files and directories that were skipped.
27
+ ObsoHTML can be run in “verbose” mode by appending `-v` or `--verbose` to the command. This will show information about files and directories that were skipped.
26
28
 
27
29
  ##### Example Commands
28
30
 
@@ -44,37 +46,32 @@ Specify a folder using a relative path:
44
46
  npx obsohtml -f ../path/to/folder
45
47
  ```
46
48
 
47
- ### 2. As a Standalone Script
48
-
49
- #### Installation
50
-
51
- Download or fork [the source repository](https://github.com/j9t/obsohtml).
49
+ ### 2. Programmatic API
52
50
 
53
- #### Execution
51
+ ObsoHTML can be imported as a module to check HTML strings in your own tooling:
54
52
 
55
- As mentioned above, the script accepts a folder (`-f`, `--folder`) and can be run in “verbose” mode (`-v`, `--verbose`).
53
+ ```js
54
+ import { checkMarkup, obsoleteElements, obsoleteAttributes } from 'obsohtml';
56
55
 
57
- ##### Example Commands
56
+ const { elements, attributes } = checkMarkup('<center>Hello</center>');
57
+ // elements: ['center']
58
+ // attributes: []
59
+ ```
58
60
 
59
- (All commands as run from the root directory of the downloaded repository.)
61
+ #### `checkMarkup(html)`
60
62
 
61
- Use the default directory (user home directory):
63
+ Checks an HTML string for obsolete or proprietary elements and attributes.
62
64
 
63
- ```console
64
- node bin/obsohtml.js
65
- ```
65
+ * **Parameter**: `html` (string)—the HTML content to check
66
+ * **Returns**: `{ elements: string[], attributes: string[] }`—arrays of found obsolete element and attribute names
66
67
 
67
- Specify a folder using an absolute path (easiest and most common use case):
68
+ #### `obsoleteElements`
68
69
 
69
- ```console
70
- node bin/obsohtml.js -f /path/to/folder
71
- ```
70
+ Array of obsolete or proprietary HTML element names checked by ObsoHTML.
72
71
 
73
- Specify a folder using a relative path:
72
+ #### `obsoleteAttributes`
74
73
 
75
- ```console
76
- node bin/obsohtml.js -f ../path/to/folder
77
- ```
74
+ Array of obsolete or proprietary HTML attribute names checked by ObsoHTML.
78
75
 
79
76
  ## Output
80
77
 
package/bin/obsohtml.js CHANGED
@@ -5,32 +5,10 @@ import fs from 'node:fs';
5
5
  import os from 'node:os';
6
6
  import path from 'node:path';
7
7
  import { styleText } from 'node:util';
8
+ import { checkMarkup } from '../src/index.js';
8
9
 
9
10
  const program = new Command();
10
11
 
11
- // List of obsolete or proprietary HTML elements
12
- const obsoleteElements = [
13
- 'acronym', 'applet', 'basefont', 'bgsound', 'big', 'blink', 'center', 'command', 'content', 'dir', 'element', 'font', 'frame', 'frameset', 'image', 'isindex', 'keygen', 'listing', 'marquee', 'menuitem', 'multicol', 'nextid', 'nobr', 'noembed', 'noframes', 'param', 'plaintext', 'rb', 'rtc', 'shadow', 'spacer', 'strike', 'tt', 'xmp'
14
- ];
15
-
16
- // List of obsolete or proprietary HTML attributes
17
- const obsoleteAttributes = [
18
- 'align', 'background', 'bgcolor', 'border', 'frameborder', 'hspace', 'marginheight', 'marginwidth', 'noshade', 'nowrap', 'scrolling', 'valign', 'vspace'
19
- ];
20
-
21
- // Pre-compile regexes once at startup
22
- const elementRegexes = obsoleteElements.map(element => ({
23
- element,
24
- regex: new RegExp(`<\\s*${element}\\b`, 'i'),
25
- }));
26
-
27
- const attributeRegexes = obsoleteAttributes.map(attribute => ({
28
- attribute,
29
- // Matches the attribute preceded by whitespace anywhere in a tag, without
30
- // requiring it to be the last attribute before the closing bracket.
31
- regex: new RegExp(`<[^>]*\\s${attribute}\\b(\\s*=\\s*(?:"[^"]*"|'[^']*'|[^"'\\s>]+))?`, 'i'),
32
- }));
33
-
34
12
  // Directories to skip during traversal
35
13
  const EXCLUDED_DIRS = new Set(['node_modules', '.git', 'dist', 'build', 'vendor']);
36
14
 
@@ -42,24 +20,16 @@ let foundObsolete = false;
42
20
 
43
21
  // Function to find obsolete elements and attributes in a file
44
22
  function findObsolete(filePath) {
45
- const content = fs.readFileSync(filePath, 'utf8');
46
-
47
- // Check for obsolete elements
48
- for (const { element, regex } of elementRegexes) {
49
- if (regex.test(content)) {
50
- foundObsolete = true;
51
- const message = styleText('blue', `Found obsolete element ${styleText('bold', `'${element}'`)} in ${filePath}`);
52
- console.log(message);
53
- }
23
+ const { elements, attributes } = checkMarkup(fs.readFileSync(filePath, 'utf8'));
24
+
25
+ for (const element of elements) {
26
+ foundObsolete = true;
27
+ console.log(styleText('blue', `Found obsolete element ${styleText('bold', `'${element}'`)} in ${filePath}`));
54
28
  }
55
29
 
56
- // Check for obsolete attributes
57
- for (const { attribute, regex } of attributeRegexes) {
58
- if (regex.test(content)) {
59
- foundObsolete = true;
60
- const message = styleText('green', `Found obsolete attribute ${styleText('bold', `'${attribute}'`)} in ${filePath}`);
61
- console.log(message);
62
- }
30
+ for (const attribute of attributes) {
31
+ foundObsolete = true;
32
+ console.log(styleText('green', `Found obsolete attribute ${styleText('bold', `'${attribute}'`)} in ${filePath}`));
63
33
  }
64
34
  }
65
35
 
@@ -5,6 +5,7 @@ import { fileURLToPath } from 'node:url';
5
5
  import { describe, test, before, after } from 'node:test';
6
6
  import assert from 'node:assert';
7
7
  import { stripVTControlCharacters } from 'node:util';
8
+ import { checkMarkup, obsoleteElements, obsoleteAttributes } from '../src/index.js';
8
9
 
9
10
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
10
11
  const scriptPath = path.join(__dirname, 'obsohtml.js');
@@ -120,4 +121,78 @@ describe('ObsoHTML', () => {
120
121
  const { stderr } = run(['-f', path.join(tempDir, 'nonexistent'), '-v']);
121
122
  assert.ok(stderr.includes('Skipping non-existent directory'));
122
123
  });
124
+ });
125
+
126
+ describe('`obsoleteElements`', () => {
127
+ test('Export as a non-empty array of strings', () => {
128
+ assert.ok(Array.isArray(obsoleteElements));
129
+ assert.ok(obsoleteElements.length > 0);
130
+ assert.ok(obsoleteElements.every(e => typeof e === 'string'));
131
+ });
132
+ });
133
+
134
+ describe('`obsoleteAttributes`', () => {
135
+ test('Export as a non-empty array of strings', () => {
136
+ assert.ok(Array.isArray(obsoleteAttributes));
137
+ assert.ok(obsoleteAttributes.length > 0);
138
+ assert.ok(obsoleteAttributes.every(a => typeof a === 'string'));
139
+ });
140
+ });
141
+
142
+ describe('`checkMarkup`', () => {
143
+ test('Return empty arrays for clean HTML', () => {
144
+ const result = checkMarkup('<p>Hello <strong>world</strong></p>');
145
+ assert.deepEqual(result, { elements: [], attributes: [] });
146
+ });
147
+
148
+ test('Return empty arrays for an empty string', () => {
149
+ const result = checkMarkup('');
150
+ assert.deepEqual(result, { elements: [], attributes: [] });
151
+ });
152
+
153
+ test('Throw a TypeError for non-string input', () => {
154
+ assert.throws(() => checkMarkup(null), TypeError);
155
+ assert.throws(() => checkMarkup(42), TypeError);
156
+ });
157
+
158
+ test('Detect an obsolete element', () => {
159
+ const { elements, attributes } = checkMarkup('<center>Hello</center>');
160
+ assert.ok(elements.includes('center'));
161
+ assert.deepEqual(attributes, []);
162
+ });
163
+
164
+ test('Detect an obsolete attribute', () => {
165
+ const { elements, attributes } = checkMarkup('<img src="x.jpg" align="left">');
166
+ assert.deepEqual(elements, []);
167
+ assert.ok(attributes.includes('align'));
168
+ });
169
+
170
+ test('Detect multiple obsolete elements in one string', () => {
171
+ const { elements } = checkMarkup('<marquee><blink>Hello</blink></marquee>');
172
+ assert.ok(elements.includes('marquee'));
173
+ assert.ok(elements.includes('blink'));
174
+ });
175
+
176
+ test('Detect multiple obsolete attributes in one string', () => {
177
+ const { attributes } = checkMarkup('<table border="1" bgcolor="#fff"><tr valign="top"></tr></table>');
178
+ assert.ok(attributes.includes('border'));
179
+ assert.ok(attributes.includes('bgcolor'));
180
+ assert.ok(attributes.includes('valign'));
181
+ });
182
+
183
+ test('Detect both obsolete elements and attributes in one string', () => {
184
+ const { elements, attributes } = checkMarkup('<center><img align="left"></center>');
185
+ assert.ok(elements.includes('center'));
186
+ assert.ok(attributes.includes('align'));
187
+ });
188
+
189
+ test('Detect obsolete elements case-insensitively', () => {
190
+ const { elements } = checkMarkup('<CENTER>Hello</CENTER>');
191
+ assert.ok(elements.includes('center'));
192
+ });
193
+
194
+ test('Do not detect partial tag name matches', () => {
195
+ const { elements } = checkMarkup('<centers>Hello</centers>');
196
+ assert.deepEqual(elements, []);
197
+ });
123
198
  });
package/package.json CHANGED
@@ -7,6 +7,18 @@
7
7
  "commander": "^14.0.0"
8
8
  },
9
9
  "description": "HTML checker for obsolete and proprietary elements and attributes",
10
+ "devDependencies": {
11
+ "@eslint/js": "^10.0.0",
12
+ "eslint": "^10.1.0",
13
+ "globals": "^17.0.0",
14
+ "typescript": "^6.0.2"
15
+ },
16
+ "exports": {
17
+ ".": {
18
+ "types": "./src/index.d.ts",
19
+ "default": "./src/index.js"
20
+ }
21
+ },
10
22
  "funding": "https://github.com/j9t/obsohtml?sponsor=1",
11
23
  "homepage": "https://github.com/j9t/obsohtml",
12
24
  "keywords": [
@@ -16,22 +28,20 @@
16
28
  "semantics"
17
29
  ],
18
30
  "license": "MIT",
31
+ "main": "src/index.js",
19
32
  "name": "obsohtml",
20
33
  "repository": {
21
34
  "type": "git",
22
35
  "url": "git+https://github.com/j9t/obsohtml.git"
23
36
  },
24
- "devDependencies": {
25
- "@eslint/js": "^10.0.0",
26
- "eslint": "^10.0.0",
27
- "globals": "^17.0.0"
28
- },
29
37
  "scripts": {
30
38
  "lint": "eslint .",
31
39
  "lint:fix": "eslint . --fix",
32
40
  "start": "node ./bin/obsohtml.js",
33
- "test": "node --test bin/obsohtml.test.js"
41
+ "test": "npm run test:types && node --test bin/obsohtml.test.js",
42
+ "test:types": "tsc"
34
43
  },
35
44
  "type": "module",
36
- "version": "1.9.9"
45
+ "types": "src/index.d.ts",
46
+ "version": "1.10.0"
37
47
  }
package/src/index.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ export declare const obsoleteElements: readonly string[];
2
+ export declare const obsoleteAttributes: readonly string[];
3
+
4
+ export declare function checkMarkup(html: string): {
5
+ elements: string[];
6
+ attributes: string[];
7
+ };
package/src/index.js ADDED
@@ -0,0 +1,46 @@
1
+ // Obsolete or proprietary HTML elements
2
+ export const obsoleteElements = Object.freeze([
3
+ 'acronym', 'applet', 'basefont', 'bgsound', 'big', 'blink', 'center', 'command', 'content', 'dir', 'element', 'font', 'frame', 'frameset', 'image', 'isindex', 'keygen', 'listing', 'marquee', 'menuitem', 'multicol', 'nextid', 'nobr', 'noembed', 'noframes', 'param', 'plaintext', 'rb', 'rtc', 'shadow', 'spacer', 'strike', 'tt', 'xmp'
4
+ ]);
5
+
6
+ // Obsolete or proprietary HTML attributes
7
+ export const obsoleteAttributes = Object.freeze([
8
+ 'align', 'background', 'bgcolor', 'border', 'frameborder', 'hspace', 'marginheight', 'marginwidth', 'noshade', 'nowrap', 'scrolling', 'valign', 'vspace'
9
+ ]);
10
+
11
+ // Pre-compiled regexes for performance
12
+ const elementRegexes = new Map(
13
+ obsoleteElements.map(element => [element, new RegExp(`<\\s*${element}\\b`, 'i')])
14
+ );
15
+
16
+ const attributeRegexes = new Map(
17
+ obsoleteAttributes.map(attribute => [
18
+ attribute,
19
+ // Matches the attribute preceded by whitespace anywhere in a tag, without
20
+ // requiring it to be the last attribute before the closing bracket.
21
+ new RegExp(`<[^>]*\\s${attribute}\\b(\\s*=\\s*(?:"[^"]*"|'[^']*'|[^"'\\s>]+))?`, 'i'),
22
+ ])
23
+ );
24
+
25
+ /**
26
+ * Check an HTML string for obsolete or proprietary elements and attributes.
27
+ *
28
+ * @param {string} html
29
+ * @returns {{ elements: string[], attributes: string[] }}
30
+ */
31
+ export function checkMarkup(html) {
32
+ if (typeof html !== 'string') throw new TypeError('`html` must be a string');
33
+
34
+ const elements = [];
35
+ const attributes = [];
36
+
37
+ for (const [element, regex] of elementRegexes) {
38
+ if (regex.test(html)) elements.push(element);
39
+ }
40
+
41
+ for (const [attribute, regex] of attributeRegexes) {
42
+ if (regex.test(html)) attributes.push(attribute);
43
+ }
44
+
45
+ return { elements, attributes };
46
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * TypeScript type definition tests
3
+ *
4
+ * This file is compiled by TypeScript during testing to verify that the
5
+ * declaration file is valid and types are correctly exported and usable.
6
+ *
7
+ * This file is not executed—it only needs to type-check successfully.
8
+ */
9
+
10
+ import { checkMarkup, obsoleteElements, obsoleteAttributes } from './index.js';
11
+
12
+ // Exported arrays are readonly string arrays
13
+ const els: readonly string[] = obsoleteElements;
14
+ const attrs: readonly string[] = obsoleteAttributes;
15
+
16
+ // `checkMarkup` accepts a string and returns the correct shape
17
+ const result: { elements: string[]; attributes: string[] } = checkMarkup('<center>test</center>');
18
+
19
+ // `checkMarkup` rejects non-string input
20
+ // @ts-expect-error
21
+ checkMarkup(null);
22
+ // @ts-expect-error
23
+ checkMarkup(42);
24
+
25
+ export { els, attrs, result };
package/tsconfig.json ADDED
@@ -0,0 +1,16 @@
1
+ {
2
+ "include": [
3
+ "src/index.test.ts"
4
+ ],
5
+ "compilerOptions": {
6
+ "module": "nodenext",
7
+ "target": "esnext",
8
+ "noEmit": true,
9
+ "strict": true,
10
+ "skipLibCheck": true,
11
+ "verbatimModuleSyntax": true,
12
+ "isolatedModules": true,
13
+ "moduleDetection": "force",
14
+ "forceConsistentCasingInFileNames": true
15
+ }
16
+ }