@produtype/core 0.68.0 → 0.70.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.
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The lines of a C-family file that sit inside a block comment.
|
|
3
|
+
*
|
|
4
|
+
* The comment rule reads one line at a time and looks for a marker at its start:
|
|
5
|
+
* a double slash, a slash-star, a lone star. A block comment written without a
|
|
6
|
+
* leading star on every line has no marker to find, and neither does code that
|
|
7
|
+
* somebody commented out by wrapping the whole thing. In AsteroidsJS a wrapped
|
|
8
|
+
* `localStorage.setItem('highScores', ...)` was cited as evidence that the game
|
|
9
|
+
* saves progress; the function it belongs to is switched off.
|
|
10
|
+
*
|
|
11
|
+
* It is the same defect the Python docstrings had, in the languages where the marker
|
|
12
|
+
* usually is there and sometimes is not.
|
|
13
|
+
*
|
|
14
|
+
* Counting the delimiters is not enough, and measuring that was the point. A first
|
|
15
|
+
* pass said 135 citations across the corpus, and the second example it offered was
|
|
16
|
+
* live code: a glob like star-star-slash-star-dot-ts, and a slash-star inside a
|
|
17
|
+
* shader string, both look like an opening. So this walks the file properly —
|
|
18
|
+
* through single quotes, double quotes, template literals and line comments — and
|
|
19
|
+
* only then decides.
|
|
20
|
+
*/
|
|
21
|
+
export declare function blockCommentLines(file: string, text: string): Set<number>;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* The lines of a C-family file that sit inside a block comment.
|
|
4
|
+
*
|
|
5
|
+
* The comment rule reads one line at a time and looks for a marker at its start:
|
|
6
|
+
* a double slash, a slash-star, a lone star. A block comment written without a
|
|
7
|
+
* leading star on every line has no marker to find, and neither does code that
|
|
8
|
+
* somebody commented out by wrapping the whole thing. In AsteroidsJS a wrapped
|
|
9
|
+
* `localStorage.setItem('highScores', ...)` was cited as evidence that the game
|
|
10
|
+
* saves progress; the function it belongs to is switched off.
|
|
11
|
+
*
|
|
12
|
+
* It is the same defect the Python docstrings had, in the languages where the marker
|
|
13
|
+
* usually is there and sometimes is not.
|
|
14
|
+
*
|
|
15
|
+
* Counting the delimiters is not enough, and measuring that was the point. A first
|
|
16
|
+
* pass said 135 citations across the corpus, and the second example it offered was
|
|
17
|
+
* live code: a glob like star-star-slash-star-dot-ts, and a slash-star inside a
|
|
18
|
+
* shader string, both look like an opening. So this walks the file properly —
|
|
19
|
+
* through single quotes, double quotes, template literals and line comments — and
|
|
20
|
+
* only then decides.
|
|
21
|
+
*/
|
|
22
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
|
+
exports.blockCommentLines = blockCommentLines;
|
|
24
|
+
const C_FAMILY = /\.(ts|tsx|js|jsx|mjs|cjs|go|java|cs|rs|php|scala|kt|swift|c|cc|cpp|h)$/i;
|
|
25
|
+
function blockCommentLines(file, text) {
|
|
26
|
+
const inside = new Set();
|
|
27
|
+
if (!C_FAMILY.test(file))
|
|
28
|
+
return inside;
|
|
29
|
+
if (!text.includes('/*'))
|
|
30
|
+
return inside;
|
|
31
|
+
let line = 1;
|
|
32
|
+
let state = 'code';
|
|
33
|
+
let escaped = false;
|
|
34
|
+
for (let i = 0; i < text.length; i++) {
|
|
35
|
+
const character = text[i];
|
|
36
|
+
const next = text[i + 1];
|
|
37
|
+
if (character === '\n') {
|
|
38
|
+
line++;
|
|
39
|
+
if (state === 'line-comment')
|
|
40
|
+
state = 'code';
|
|
41
|
+
if (state === 'single' || state === 'double')
|
|
42
|
+
state = 'code';
|
|
43
|
+
if (state === 'block-comment')
|
|
44
|
+
inside.add(line);
|
|
45
|
+
escaped = false;
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
if (escaped) {
|
|
49
|
+
escaped = false;
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
switch (state) {
|
|
53
|
+
case 'code':
|
|
54
|
+
if (character === '/' && next === '/') {
|
|
55
|
+
state = 'line-comment';
|
|
56
|
+
i++;
|
|
57
|
+
}
|
|
58
|
+
else if (character === '/' && next === '*') {
|
|
59
|
+
state = 'block-comment';
|
|
60
|
+
i++;
|
|
61
|
+
}
|
|
62
|
+
else if (character === "'")
|
|
63
|
+
state = 'single';
|
|
64
|
+
else if (character === '"')
|
|
65
|
+
state = 'double';
|
|
66
|
+
else if (character === '`')
|
|
67
|
+
state = 'template';
|
|
68
|
+
break;
|
|
69
|
+
case 'block-comment':
|
|
70
|
+
if (character === '*' && next === '/') {
|
|
71
|
+
state = 'code';
|
|
72
|
+
i++;
|
|
73
|
+
}
|
|
74
|
+
break;
|
|
75
|
+
case 'single':
|
|
76
|
+
case 'double':
|
|
77
|
+
case 'template':
|
|
78
|
+
if (character === '\\')
|
|
79
|
+
escaped = true;
|
|
80
|
+
else if ((state === 'single' && character === "'")
|
|
81
|
+
|| (state === 'double' && character === '"')
|
|
82
|
+
|| (state === 'template' && character === '`')) {
|
|
83
|
+
state = 'code';
|
|
84
|
+
}
|
|
85
|
+
break;
|
|
86
|
+
default:
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return inside;
|
|
91
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The lines of a Python file that are a docstring rather than code.
|
|
3
|
+
*
|
|
4
|
+
* This analyzer has skipped comments since the day it read its own prose about Stripe
|
|
5
|
+
* as evidence that it takes payments. The rule looks for a line marker — `#`, `//`,
|
|
6
|
+
* `/*`, `<!--` — and Python's main way of writing prose has none: a docstring is a
|
|
7
|
+
* string, and its interior lines begin with whatever the author was saying.
|
|
8
|
+
*
|
|
9
|
+
* Measured across the corpus: 118 of 1587 citations from Python files land inside
|
|
10
|
+
* one. `/api/auth/... registrazione + JWT` was cited as evidence of an
|
|
11
|
+
* authentication route; `Chi arriva col link (?k=...) si vede posare un cookie` as
|
|
12
|
+
* evidence about cookies. Sentences describing the code, offered as the code.
|
|
13
|
+
*
|
|
14
|
+
* Only a block that stands on its own. `QUERY = """SELECT ..."""` is data assigned to
|
|
15
|
+
* a name and a hardcoded secret could live in one, so a triple-quoted string on the
|
|
16
|
+
* right of an assignment stays readable. A docstring is an expression statement and
|
|
17
|
+
* has nothing before it but indentation.
|
|
18
|
+
*/
|
|
19
|
+
export declare function proseLines(file: string, text: string): Set<number>;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.proseLines = proseLines;
|
|
4
|
+
/**
|
|
5
|
+
* The lines of a Python file that are a docstring rather than code.
|
|
6
|
+
*
|
|
7
|
+
* This analyzer has skipped comments since the day it read its own prose about Stripe
|
|
8
|
+
* as evidence that it takes payments. The rule looks for a line marker — `#`, `//`,
|
|
9
|
+
* `/*`, `<!--` — and Python's main way of writing prose has none: a docstring is a
|
|
10
|
+
* string, and its interior lines begin with whatever the author was saying.
|
|
11
|
+
*
|
|
12
|
+
* Measured across the corpus: 118 of 1587 citations from Python files land inside
|
|
13
|
+
* one. `/api/auth/... registrazione + JWT` was cited as evidence of an
|
|
14
|
+
* authentication route; `Chi arriva col link (?k=...) si vede posare un cookie` as
|
|
15
|
+
* evidence about cookies. Sentences describing the code, offered as the code.
|
|
16
|
+
*
|
|
17
|
+
* Only a block that stands on its own. `QUERY = """SELECT ..."""` is data assigned to
|
|
18
|
+
* a name and a hardcoded secret could live in one, so a triple-quoted string on the
|
|
19
|
+
* right of an assignment stays readable. A docstring is an expression statement and
|
|
20
|
+
* has nothing before it but indentation.
|
|
21
|
+
*/
|
|
22
|
+
function proseLines(file, text) {
|
|
23
|
+
const prose = new Set();
|
|
24
|
+
if (!/\.py$/i.test(file))
|
|
25
|
+
return prose;
|
|
26
|
+
const lines = text.split(/\r?\n/);
|
|
27
|
+
let openQuote = null;
|
|
28
|
+
let openIsProse = false;
|
|
29
|
+
for (let i = 0; i < lines.length; i++) {
|
|
30
|
+
const line = lines[i];
|
|
31
|
+
if (openQuote) {
|
|
32
|
+
if (openIsProse)
|
|
33
|
+
prose.add(i + 1);
|
|
34
|
+
if (line.includes(openQuote)) {
|
|
35
|
+
openQuote = null;
|
|
36
|
+
openIsProse = false;
|
|
37
|
+
}
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
const match = /("""|''')/.exec(line);
|
|
41
|
+
if (!match)
|
|
42
|
+
continue;
|
|
43
|
+
const before = line.slice(0, match.index);
|
|
44
|
+
/** An expression statement: nothing before the quotes but whitespace. */
|
|
45
|
+
const standsAlone = /^\s*[rRbBuUfF]*$/.test(before);
|
|
46
|
+
const rest = line.slice(match.index + 3);
|
|
47
|
+
const closesOnThisLine = rest.includes(match[1]);
|
|
48
|
+
if (standsAlone)
|
|
49
|
+
prose.add(i + 1);
|
|
50
|
+
if (closesOnThisLine)
|
|
51
|
+
continue;
|
|
52
|
+
openQuote = match[1];
|
|
53
|
+
openIsProse = standsAlone;
|
|
54
|
+
}
|
|
55
|
+
return prose;
|
|
56
|
+
}
|
package/dist/utils/textSearch.js
CHANGED
|
@@ -6,6 +6,8 @@ exports.searchInFiles = searchInFiles;
|
|
|
6
6
|
exports.anyIncludes = anyIncludes;
|
|
7
7
|
const readTextFileSafe_1 = require("./readTextFileSafe");
|
|
8
8
|
const developmentOnly_1 = require("../analyzer/developmentOnly");
|
|
9
|
+
const proseLines_1 = require("../analyzer/proseLines");
|
|
10
|
+
const blockComments_1 = require("../analyzer/blockComments");
|
|
9
11
|
/**
|
|
10
12
|
* A line that declares a pattern rather than doing anything.
|
|
11
13
|
*
|
|
@@ -103,9 +105,13 @@ function matchLines(text, needles, file = '') {
|
|
|
103
105
|
* fixture of a test asserting the wrong secret is rejected.
|
|
104
106
|
*/
|
|
105
107
|
const testOnly = (0, developmentOnly_1.testOnlyLines)(file, text);
|
|
108
|
+
/** A Python docstring is prose with no line marker to recognise it by. */
|
|
109
|
+
const prose = (0, proseLines_1.proseLines)(file, text);
|
|
110
|
+
/** Code somebody switched off by wrapping it, which has no marker on its lines. */
|
|
111
|
+
const commented = (0, blockComments_1.blockCommentLines)(file, text);
|
|
106
112
|
for (let i = 0; i < lines.length; i++) {
|
|
107
113
|
const line = lines[i];
|
|
108
|
-
if (testOnly.has(i + 1))
|
|
114
|
+
if (testOnly.has(i + 1) || prose.has(i + 1) || commented.has(i + 1))
|
|
109
115
|
continue;
|
|
110
116
|
if (line.length > MAX_CITABLE_LINE)
|
|
111
117
|
continue;
|
|
@@ -135,9 +141,11 @@ async function searchInFiles(root, files, needles, limit = 25) {
|
|
|
135
141
|
continue;
|
|
136
142
|
const lines = text.split(/\r?\n/);
|
|
137
143
|
const testOnly = (0, developmentOnly_1.testOnlyLines)(file, text);
|
|
144
|
+
const prose = (0, proseLines_1.proseLines)(file, text);
|
|
145
|
+
const commented = (0, blockComments_1.blockCommentLines)(file, text);
|
|
138
146
|
for (let i = 0; i < lines.length; i++) {
|
|
139
147
|
const line = lines[i];
|
|
140
|
-
if (testOnly.has(i + 1))
|
|
148
|
+
if (testOnly.has(i + 1) || prose.has(i + 1) || commented.has(i + 1))
|
|
141
149
|
continue;
|
|
142
150
|
if (line.length > MAX_CITABLE_LINE)
|
|
143
151
|
continue;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@produtype/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.70.0",
|
|
4
4
|
"description": "Deterministic CLI and library that analyzes a web application repository and reports how far it is from production-ready for the kind of product it is meant to be.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bin": {
|