Skip to content

Commit c71f458

Browse files
committed
improve automatic type detection at variable assignment time
less error-prone, and now also supports more complex use cases such as loops and destructuring assignments. This was possible by switching the CoffeeScript compiler to a recent contribution by @edemaine at jashkenas/coffeescript#5395
1 parent f65f758 commit c71f458

File tree

2 files changed

+1
-128
lines changed

2 files changed

+1
-128
lines changed

server/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
},
2323
"homepage": "https://github.com/phil294/coffeesense/tree/master/server",
2424
"dependencies": {
25+
"coffeescript": "github:edemaine/coffeescript#var-assign",
2526
"jshashes": "^1.0.8",
2627
"typescript": "^4.3.2",
2728
"volatile-map": "^1.0.2"
@@ -36,7 +37,6 @@
3637
"@types/resolve": "1.20.0",
3738
"@typescript-eslint/eslint-plugin": "^5.9.0",
3839
"@typescript-eslint/parser": "^5.9.0",
39-
"coffeescript": "^2.5.1",
4040
"core-js": "^3.15.1",
4141
"eslint": "^8.6.0",
4242
"fast-glob": "^3.2.5",

server/src/services/transpileService.ts

Lines changed: 0 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -130,133 +130,6 @@ function postprocess_js(result: ITranspilationResult) {
130130
// but they do no harm and should go unnoticed
131131
result.js = result.js.replaceAll(/𒐛: 𒐛,?/g, '')
132132

133-
// console.time('var-decl-fix')
134-
//////////////////////////////////////
135-
///////// Modify variable declarations to solve various TS compiler errors:
136-
// Should not be error but is:
137-
// xy = 123 # Error: Variable 'xy' implicitly has type 'any' in some locations where its type cannot be determined.CoffeeSense [TS](7034)
138-
// => xy # Error: Variable 'xy' implicitly has an 'any' type.CoffeeSense [TS](7005)
139-
//////// and
140-
// Should be error but is not:
141-
// a = 1
142-
// a = 'one'
143-
/////// This is because the cs compiler puts variable declarations to the front:
144-
// Translates to:
145-
// var a;
146-
// a = 1;
147-
// a = 'one';
148-
/////// and now `a` is of type `number | string` (https://github.com/microsoft/TypeScript/issues/45369).
149-
// Below is a hacky workaround that should fix these issues in most cases. It moves the
150-
// declaration part (`var`) down to the variable's first implementation position.
151-
// This works only with easy implementations and single-variable array destructuring:
152-
/*
153-
var a, b, c;
154-
a = 1;
155-
[b] = 2;
156-
({c} = 3);
157-
*/
158-
// Shall become:
159-
/*
160-
var c;
161-
let a = 1; // added let
162-
let [b] = 2; // added let
163-
({c} = 3); // unchanged because of surrounding braces
164-
*/
165-
// similarly, array destructors with more than one variable cannot be changed.
166-
// Returns stay untouched (return x = 1) too.
167-
const js_lines = result.js.split('\n')
168-
const js_line_nos = Array.from(Array(js_lines.length).keys())
169-
// Part 1: Determine declaration areas (` var x, y;`)
170-
const js_decl_lines_info = js_line_nos
171-
.map(decl_line_no => {
172-
const match = js_lines[decl_line_no]!.match(/^(\s*)(var )(.+);$/)
173-
if(match) {
174-
const var_decl_infos = match[3]!.split(', ').map(var_name => ({
175-
var_name,
176-
decl_indent: match[1]!.length,
177-
decl_line_no,
178-
}))
179-
return {
180-
decl_line_no,
181-
var_decl_infos,
182-
}
183-
}
184-
return null
185-
})
186-
.filter(Boolean)
187-
// Part 2: For each `var` decl, find fitting first impl statement
188-
// (`x = 1`), if present, and return new line content (`let x = 1`).
189-
// Might as well be `var x = 1` but this helps differentiating/debugging
190-
const js_impl_line_changes = js_decl_lines_info
191-
.map(info => info!.var_decl_infos)
192-
.flat()
193-
.map(({ var_name, decl_indent, decl_line_no }) => {
194-
const js_line_nos_after_decl = js_line_nos.slice(decl_line_no)
195-
for(const impl_line_no of js_line_nos_after_decl) {
196-
const line = js_lines[impl_line_no]!
197-
const impl_whitespace = line.match(/^\s*/)![0]!
198-
const impl_indent = impl_whitespace.length
199-
if(impl_indent < decl_indent)
200-
// Parent block scope. Need to skip this variable then, no impl has been found
201-
// before current block got closed. It is important to stop here, as otherwise
202-
// it might later match an impl from *another* decl of the same var name
203-
return null
204-
const var_impl_text = `${var_name} = `
205-
if(line.substr(impl_indent, var_impl_text.length) === var_impl_text) {
206-
if(impl_indent > decl_indent)
207-
// This is a conditional first value assignment and type can not safely be set
208-
return null
209-
const rest_of_line = line.slice(impl_indent + var_impl_text.length)
210-
return {
211-
var_name,
212-
impl_line_no,
213-
decl_line_no,
214-
new_line_content: `${impl_whitespace}let ${var_impl_text}${rest_of_line}`,
215-
new_let_column: impl_indent,
216-
}
217-
}
218-
}
219-
return null
220-
}).filter(Boolean)
221-
// Part 3: Apply Part 2 changes and update source maps of those lines
222-
for(const change of js_impl_line_changes) {
223-
js_lines[change!.impl_line_no] = change!.new_line_content
224-
const map_columns = result.source_map[change!.impl_line_no]!.columns
225-
const map_current_impl_start = map_columns[change!.new_let_column]!
226-
// Can be null in cases where the variable is not user-set but e.g. a helper
227-
// variable put there by the cs compiler itself and ignored otherwise
228-
if(map_current_impl_start != null) {
229-
map_columns.splice(
230-
change!.new_let_column,
231-
0,
232-
..."let ".split('').map((_, i) => ({
233-
...map_current_impl_start,
234-
column: map_current_impl_start.column + i
235-
})))
236-
for(let i = map_current_impl_start.column + "let ".length; i < map_columns.length + "let ".length; i++) {
237-
if(map_columns[i])
238-
map_columns[i]!.column += "let ".length // or = i
239-
}
240-
}
241-
}
242-
// Part 4: Update decl lines (Part 1). Where no impl lines were found (Part 2),
243-
// keep them. If all were modified, an empty line will be put.
244-
for(const decl_line_info of js_decl_lines_info) {
245-
let new_decl_line = decl_line_info!.var_decl_infos
246-
.filter(decl_info => ! js_impl_line_changes.some(impl_change =>
247-
impl_change!.var_name === decl_info.var_name &&
248-
impl_change!.decl_line_no === decl_info.decl_line_no))
249-
.map(i => i.var_name)
250-
.join(', ')
251-
if(new_decl_line)
252-
// Those that could not be changed
253-
new_decl_line = 'var ' + new_decl_line
254-
js_lines[decl_line_info!.decl_line_no] = new_decl_line
255-
}
256-
257-
result.js = js_lines.join('\n')
258-
// console.timeEnd('var-decl-fix')
259-
260133
/* Prefer object method shorthand */
261134
result.js = result.js.replaceAll(/([a-zA-Z0-9_$]+): (async )?function(\*?)\(/g, (_, func_name, asynk, asterisk) =>
262135
`${asynk || ''}${asterisk}${func_name} (`

0 commit comments

Comments
 (0)