true 2.1.3 → 2.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a6a3b24624df5c06f4fcdb1da05e91c508e43d1f
4
- data.tar.gz: dc4fbcdbd8bd5e8ad8d33a4354ee8eacb94e1312
3
+ metadata.gz: 43b01a67a1a718450d44cbfde4148435092d885e
4
+ data.tar.gz: 2a9662ae8300f2983a2de95e1eb5555a3b5f6138
5
5
  SHA512:
6
- metadata.gz: f0bc90f7aecd98a9fcf877487c13cfccff8b5ef1617d37e86e44b82d412e3f63b9f5199cd743ffa44df248845339252cbc6d059605928e6627f7cfe2bfba8768
7
- data.tar.gz: 1dfa2bcd589cdb764dda6720c0cf0dd3fdc244c2831e62d215df77c6b579cb0b6fb7fd1fb2227fc0bdc8f10be16d3f436988ce9c3f70b0bd94ba0dcc150c377a
6
+ metadata.gz: aad2c6319c983bad8e96b4364c936d7f0f18a2a3ad2aae8ff5e042fe0f9cdebf873079b9074f65681a2f4b9ca4a7c618d9d46a6b777559999fc9e3e86dd2dba9
7
+ data.tar.gz: b62531759187d5970e96fd946df5e1031e43bd0c9e3384f25184c9ad1243aa5eefdf4649c429dd40d531aef7284cf52568e9e9db3c380fef9e596dcd56d4663b
@@ -2,6 +2,23 @@ True Changelog
2
2
  ==============
3
3
 
4
4
 
5
+ 2.2.0 UNRELEASED
6
+ ----------------
7
+ - Output CSS context around Mocha parsing errors.
8
+ - Added `$fail-on-error` argument to `report()` mixin.
9
+ Set to `true` if you need the Sass compiler to fail
10
+ on broken tests.
11
+ - Fix bug with `assert-false` causing it to fail on `null` values.
12
+ - Allow unquoted descriptions and test/module names.
13
+ - Fix bug throwing off test-count and reporting.
14
+
15
+
16
+ 2.1.4 (12/22/16)
17
+ ----------------
18
+ - Fix default assertion messages
19
+ - Upgrade dependencies
20
+
21
+
5
22
  2.0.2 (5/13/15)
6
23
  ---------------
7
24
  - Fixes debug inspector.
data/README.md CHANGED
@@ -1,5 +1,4 @@
1
- True
2
- ====
1
+ # True
3
2
 
4
3
  [![Build Status](https://api.travis-ci.org/oddbird/true.svg)](https://travis-ci.org/oddbird/true)
5
4
 
@@ -13,8 +12,7 @@ True
13
12
  *True your sweet plugin before you deploy.*
14
13
 
15
14
 
16
- Install
17
- -------
15
+ ## Install
18
16
 
19
17
  in command line:
20
18
 
@@ -30,11 +28,9 @@ npm install sass-true
30
28
  ```
31
29
 
32
30
 
33
- Usage
34
- -----
31
+ ## Usage
35
32
 
36
- With any Sass compiler
37
- ~~~~~~~~~~~~~~~~~~~~~~
33
+ ### With any Sass compiler
38
34
 
39
35
  ```scss
40
36
  @import "true";
@@ -85,8 +81,7 @@ in the output code.
85
81
  Version control can make that much easier than it sounds.
86
82
 
87
83
 
88
- With node-sass and Mocha (or other JS test runners)
89
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
84
+ ### With node-sass and Mocha (or other JS test runners)
90
85
 
91
86
  1. Install `true` via npm (`npm install sass-true`).
92
87
 
@@ -94,7 +89,7 @@ With node-sass and Mocha (or other JS test runners)
94
89
 
95
90
  3. Write a shim JS test file in `test/test_sass.js`:
96
91
 
97
- ```js
92
+ ```javascript
98
93
  var path = require('path');
99
94
  var sassTrue = require('sass-true');
100
95
 
@@ -116,6 +111,12 @@ Any other JS test runner with equivalents to Mocha's `describe` and `it` should
116
111
  be usable in the same way; just pass your test runner's `describe` and `it`
117
112
  equivalents into `runSass`.
118
113
 
114
+ If True's Mocha plugin can't parse the CSS output from True, it'll give you
115
+ some context lines of CSS as part of the error message. This context will
116
+ likely be helpful in understanding the parse failure. By default it provides up
117
+ to 10 lines of context; if you need more, you can provide a numeric fourth
118
+ argument to `runSass`, the maximum number of context lines to provide.
119
+
119
120
 
120
121
  #### With Grunt...
121
122
 
@@ -124,7 +125,7 @@ Run Mocha using the Grunt task supplied by
124
125
 
125
126
  Install `grunt-mocha-cli`:
126
127
 
127
- ```
128
+ ```bash
128
129
  npm install grunt-mocha-cli --save-dev
129
130
  ```
130
131
 
@@ -144,18 +145,17 @@ Run tests:
144
145
  grunt mochacli
145
146
  ```
146
147
 
147
-
148
- With ruby-sass on the command line
149
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
148
+ ### With ruby-sass on the command line
150
149
 
151
150
  ```bash
152
151
  true-cli [options] PATH
153
152
  ```
154
153
 
155
154
  Options:
156
- * `-s` silent
157
- * `-c` config file
158
- * `-d` debug config file settings
155
+
156
+ * `-s` silent
157
+ * `-c` config file
158
+ * `-d` debug config file settings
159
159
 
160
160
  Config file (optional):
161
161
 
@@ -166,14 +166,13 @@ options:
166
166
  # require ruby sass extension libraries
167
167
  require:
168
168
  - "compass"
169
- - "serialy_sassy"
169
+ - "serialy/sassy"
170
170
  ```
171
171
 
172
172
  default location: `test/true.yml`
173
173
 
174
174
 
175
- Settings
176
- --------
175
+ ## Settings
177
176
 
178
177
  There is only one setting:
179
178
  `$true-terminal-output`
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.1.2
1
+ 2.2.0
@@ -24,7 +24,7 @@ var EXPECTED_START_TOKEN = 'EXPECTED';
24
24
  var EXPECTED_END_TOKEN = 'END_EXPECTED';
25
25
  var ASSERT_END_TOKEN = 'END_ASSERT';
26
26
 
27
- module.exports.runSass = function (options, describe, it) {
27
+ module.exports.runSass = function (options, describe, it, contextLines) {
28
28
  var sassPath = path.join(__dirname, '..', 'sass');
29
29
  if (options.includePaths) {
30
30
  options.includePaths.push(sassPath);
@@ -32,7 +32,7 @@ module.exports.runSass = function (options, describe, it) {
32
32
  options.includePaths = [sassPath];
33
33
  }
34
34
  var css = sass.renderSync(options).css.toString();
35
- var modules = parse(css);
35
+ var modules = parse(css, contextLines);
36
36
 
37
37
  _.each(modules, function (module) {
38
38
  describeModule(module, describe, it);
@@ -70,212 +70,227 @@ var describeModule = function (module, describe, it) {
70
70
  };
71
71
 
72
72
 
73
- var parse = module.exports.parse = function (rawCss) {
74
- var ast = css.parse(rawCss);
75
- var ctx = { modules: [] };
76
- var handler = parseModule;
77
-
78
- _.each(ast.stylesheet.rules, function (rule) {
79
- handler = handler(rule, ctx);
80
- });
81
-
82
- finishCurrentModule(ctx);
83
-
84
- return ctx.modules;
85
- };
73
+ var parse = module.exports.parse = function (rawCss, contextLines) {
74
+ var contextLines = (typeof contextLines === 'undefined') ? 10 : contextLines;
75
+ var lines = rawCss.split(/\r?\n/);
86
76
 
77
+ var parseCss = function () {
78
+ var ast = css.parse(rawCss);
79
+ var ctx = { modules: [] };
80
+ var handler = parseModule;
87
81
 
88
- var parseError = function (msg, seeking, pos) {
89
- return new Error(
90
- 'Line ' + pos.start.line + ', ' +
91
- 'column ' + pos.start.column + ': ' +
92
- msg + '; ' +
93
- 'looking for ' + seeking
94
- );
95
- }
82
+ _.each(ast.stylesheet.rules, function (rule) {
83
+ handler = handler(rule, ctx);
84
+ });
96
85
 
86
+ finishCurrentModule(ctx);
97
87
 
98
- var parseModule = function (rule, ctx) {
99
- if (rule.type === 'charset') { return parseModule; }
100
- if (rule.type === 'comment') {
101
- var text = rule.comment.trim();
102
- if (!text) { return parseModule; }
103
- if (startsWith(text, MODULE_TOKEN)) {
104
- finishCurrentModule(ctx);
105
- ctx.currentModule = { module: text.substring(MODULE_TOKEN.length), tests: [] };
106
- return parseTest;
107
- }
108
- if (startsWith(text, SUMMARY_TOKEN)) {
109
- return ignoreUntilEndSummary;
110
- }
111
- // ignore un-recognized comments, keep looking for module header.
112
- return parseModule;
88
+ return ctx.modules;
113
89
  }
114
- throw parseError('Unexpected rule type "' + rule.type + '"', 'module header', rule.position);
115
- };
116
90
 
117
91
 
118
- var ignoreUntilEndSummary = function (rule, ctx) {
119
- if (rule.type === 'comment') {
120
- var text = rule.comment.trim();
121
- if (startsWith(text, END_SUMMARY_TOKEN)) {
122
- return parseModule;
123
- }
124
- return ignoreUntilEndSummary;
92
+ var parseError = function (msg, seeking, pos) {
93
+ return new Error(
94
+ 'Line ' + pos.start.line + ', ' +
95
+ 'column ' + pos.start.column + ': ' +
96
+ msg + '; ' +
97
+ 'looking for ' + seeking + '.\n' +
98
+ '-- Context --\n' +
99
+ lines.slice(
100
+ Math.max(0, pos.start.line - contextLines),
101
+ pos.start.line
102
+ ).join('\n') + '\n' +
103
+ (' '.repeat(pos.start.column - 1)) + '^\n'
104
+ );
125
105
  }
126
- throw parseError('Unexpected rule type "' + rule.type + '"', 'end summary', rule.position);
127
- };
128
106
 
129
107
 
130
- var parseTest = function (rule, ctx) {
131
- if (rule.type === 'comment') {
132
- var text = rule.comment.trim();
133
- if (!text) { return parseTest; }
134
- if (text.match(/^-+$/)) {
135
- return parseTest;
136
- }
137
- if (startsWith(text, TEST_TOKEN)) {
138
- finishCurrentTest(ctx);
139
- ctx.currentTest = { test: text.substring(TEST_TOKEN.length), assertions: [] };
140
- return parseAssertion;
108
+ var parseModule = function (rule, ctx) {
109
+ if (rule.type === 'charset') { return parseModule; }
110
+ if (rule.type === 'comment') {
111
+ var text = rule.comment.trim();
112
+ if (!text) { return parseModule; }
113
+ if (startsWith(text, MODULE_TOKEN)) {
114
+ finishCurrentModule(ctx);
115
+ ctx.currentModule = { module: text.substring(MODULE_TOKEN.length), tests: [] };
116
+ return parseTest;
117
+ }
118
+ if (startsWith(text, SUMMARY_TOKEN)) {
119
+ return ignoreUntilEndSummary;
120
+ }
121
+ // ignore un-recognized comments, keep looking for module header.
122
+ return parseModule;
141
123
  }
142
- return parseModule(rule, ctx);
143
- }
144
- throw parseError('Unexpected rule type "' + rule.type + '"', 'test', rule.position);
145
- };
124
+ throw parseError('Unexpected rule type "' + rule.type + '"', 'module header', rule.position);
125
+ };
146
126
 
147
127
 
148
- var parseAssertion = function (rule, ctx) {
149
- if (rule.type === 'comment') {
150
- var text = rule.comment.trimLeft();
151
- if (!text) { return parseAssertion; }
152
- if (startsWith(text, PASS_TOKEN)) {
153
- finishCurrentAssertion(ctx);
154
- ctx.currentAssertion = {
155
- description: text.substring(PASS_TOKEN.length).trim() || '<no description>',
156
- passed: true
157
- };
158
- return parseAssertion;
159
- } else if (startsWith(text, FAIL_TOKEN)) {
160
- finishCurrentAssertion(ctx);
161
- var endAssertionType = text.indexOf(END_FAIL_TOKEN);
162
- ctx.currentAssertion = {
163
- description: text.substring(endAssertionType + 2).trim(),
164
- passed: false,
165
- assertionType: text.substring(FAIL_TOKEN.length, endAssertionType).trim(),
166
- };
167
- return parseFailureDetail;
168
- } else if (startsWith(text, ASSERT_TOKEN)) {
169
- finishCurrentAssertion(ctx);
170
- ctx.currentAssertion = {
171
- description: text.substring(ASSERT_TOKEN.length).trim(),
172
- assertionType: 'equal'
173
- };
174
- return parseAssertionOutputStart;
128
+ var ignoreUntilEndSummary = function (rule, ctx) {
129
+ if (rule.type === 'comment') {
130
+ var text = rule.comment.trim();
131
+ if (startsWith(text, END_SUMMARY_TOKEN)) {
132
+ return parseModule;
133
+ }
134
+ return ignoreUntilEndSummary;
175
135
  }
176
- return parseTest(rule, ctx);
177
- }
178
- throw parseError('Unexpected rule type "' + rule.type + '"', 'assertion', rule.position);
179
- }
136
+ throw parseError('Unexpected rule type "' + rule.type + '"', 'end summary', rule.position);
137
+ };
180
138
 
181
139
 
182
- var parseFailureDetail = function (rule, ctx) {
183
- if (rule.type === 'comment') {
184
- var text = rule.comment.trim();
185
- if (startsWith(text, FAILURE_DETAIL_TOKEN)) {
186
- var startType = text.indexOf(FAILURE_TYPE_START_TOKEN);
187
- var endType = text.indexOf(FAILURE_TYPE_END_TOKEN);
188
- var type = text.substring(startType, endType + 1);
189
- var content = text.substring(endType + 3);
190
- var outputOrExpected;
191
- if (text.substring(FAILURE_DETAIL_TOKEN.length, startType) === OUTPUT_TOKEN) {
192
- outputOrExpected = 'output';
193
- } else if (text.substring(FAILURE_DETAIL_TOKEN.length, startType) === EXPECTED_TOKEN) {
194
- outputOrExpected = 'expected';
140
+ var parseTest = function (rule, ctx) {
141
+ if (rule.type === 'comment') {
142
+ var text = rule.comment.trim();
143
+ if (!text) { return parseTest; }
144
+ if (text.match(/^-+$/)) {
145
+ return parseTest;
195
146
  }
196
- if (outputOrExpected) {
197
- ctx.currentAssertion[outputOrExpected] = type + ' ' + content;
147
+ if (startsWith(text, TEST_TOKEN)) {
148
+ finishCurrentTest(ctx);
149
+ ctx.currentTest = { test: text.substring(TEST_TOKEN.length), assertions: [] };
150
+ return parseAssertion;
151
+ }
152
+ return parseModule(rule, ctx);
153
+ }
154
+ throw parseError('Unexpected rule type "' + rule.type + '"', 'test', rule.position);
155
+ };
156
+
157
+
158
+ var parseAssertion = function (rule, ctx) {
159
+ if (rule.type === 'comment') {
160
+ var text = rule.comment.trimLeft();
161
+ if (!text) { return parseAssertion; }
162
+ if (startsWith(text, PASS_TOKEN)) {
163
+ finishCurrentAssertion(ctx);
164
+ ctx.currentAssertion = {
165
+ description: text.substring(PASS_TOKEN.length).trim() || '<no description>',
166
+ passed: true
167
+ };
168
+ return parseAssertion;
169
+ } else if (startsWith(text, FAIL_TOKEN)) {
170
+ finishCurrentAssertion(ctx);
171
+ var endAssertionType = text.indexOf(END_FAIL_TOKEN);
172
+ ctx.currentAssertion = {
173
+ description: text.substring(endAssertionType + 2).trim(),
174
+ passed: false,
175
+ assertionType: text.substring(FAIL_TOKEN.length, endAssertionType).trim(),
176
+ };
198
177
  return parseFailureDetail;
178
+ } else if (startsWith(text, ASSERT_TOKEN)) {
179
+ finishCurrentAssertion(ctx);
180
+ ctx.currentAssertion = {
181
+ description: text.substring(ASSERT_TOKEN.length).trim(),
182
+ assertionType: 'equal'
183
+ };
184
+ return parseAssertionOutputStart;
199
185
  }
186
+ return parseTest(rule, ctx);
200
187
  }
201
- return parseAssertion(rule, ctx);
188
+ throw parseError('Unexpected rule type "' + rule.type + '"', 'assertion', rule.position);
202
189
  }
203
- throw parseError('Unexpected rule type "' + rule.type + '"', 'output/expected', rule.position);
204
- };
205
190
 
206
191
 
207
- var parseAssertionOutputStart = function (rule, ctx) {
208
- if (rule.type === 'comment') {
209
- var text = rule.comment.trim();
210
- if (!text) { return parseAssertionOutputStart; }
211
- if (text === OUTPUT_START_TOKEN) {
212
- ctx.currentOutputRules = [];
213
- return parseAssertionOutput;
192
+ var parseFailureDetail = function (rule, ctx) {
193
+ if (rule.type === 'comment') {
194
+ var text = rule.comment.trim();
195
+ if (startsWith(text, FAILURE_DETAIL_TOKEN)) {
196
+ var startType = text.indexOf(FAILURE_TYPE_START_TOKEN);
197
+ var endType = text.indexOf(FAILURE_TYPE_END_TOKEN);
198
+ var type = text.substring(startType, endType + 1);
199
+ var content = text.substring(endType + 3);
200
+ var outputOrExpected;
201
+ if (text.substring(FAILURE_DETAIL_TOKEN.length, startType) === OUTPUT_TOKEN) {
202
+ outputOrExpected = 'output';
203
+ } else if (text.substring(FAILURE_DETAIL_TOKEN.length, startType) === EXPECTED_TOKEN) {
204
+ outputOrExpected = 'expected';
205
+ }
206
+ if (outputOrExpected) {
207
+ ctx.currentAssertion[outputOrExpected] = type + ' ' + content;
208
+ return parseFailureDetail;
209
+ }
210
+ }
211
+ return parseAssertion(rule, ctx);
214
212
  }
215
- throw parseError('Unexpected comment "' + text + '"', 'OUTPUT', rule.position);
216
- }
217
- throw parseError('Unexpected rule type "' + rule.type + '"', 'OUTPUT', rule.position);
218
- };
213
+ throw parseError('Unexpected rule type "' + rule.type + '"', 'output/expected', rule.position);
214
+ };
219
215
 
220
216
 
221
- var parseAssertionOutput = function (rule, ctx) {
222
- if (rule.type === 'comment') {
223
- if (rule.comment.trim() === OUTPUT_END_TOKEN) {
224
- ctx.currentAssertion.output = css.stringify(
225
- { stylesheet: { rules: ctx.currentOutputRules }});
226
- delete ctx.currentOutputRules;
227
- return parseAssertionExpectedStart;
217
+ var parseAssertionOutputStart = function (rule, ctx) {
218
+ if (rule.type === 'comment') {
219
+ var text = rule.comment.trim();
220
+ if (!text) { return parseAssertionOutputStart; }
221
+ if (text === OUTPUT_START_TOKEN) {
222
+ ctx.currentOutputRules = [];
223
+ return parseAssertionOutput;
224
+ }
225
+ throw parseError('Unexpected comment "' + text + '"', 'OUTPUT', rule.position);
228
226
  }
229
- }
230
- ctx.currentOutputRules.push(rule);
231
- return parseAssertionOutput;
232
- };
227
+ throw parseError('Unexpected rule type "' + rule.type + '"', 'OUTPUT', rule.position);
228
+ };
233
229
 
234
230
 
235
- var parseAssertionExpectedStart = function (rule, ctx) {
236
- if (rule.type === 'comment') {
237
- var text = rule.comment.trim();
238
- if (!text) { return parseAssertionExpectedStart; }
239
- if (text === EXPECTED_START_TOKEN) {
240
- ctx.currentExpectedRules = [];
241
- return parseAssertionExpected;
231
+ var parseAssertionOutput = function (rule, ctx) {
232
+ if (rule.type === 'comment') {
233
+ if (rule.comment.trim() === OUTPUT_END_TOKEN) {
234
+ ctx.currentAssertion.output = css.stringify(
235
+ { stylesheet: { rules: ctx.currentOutputRules }});
236
+ delete ctx.currentOutputRules;
237
+ return parseAssertionExpectedStart;
238
+ }
242
239
  }
243
- throw parseError('Unexpected comment "' + text + '"', 'EXPECTED', rule.position);
244
- }
245
- throw parseError('Unexpected rule type "' + rule.type + '"', 'EXPECTED', rule.position);
246
- };
247
-
248
-
249
- var parseAssertionExpected = function (rule, ctx) {
250
- if (rule.type === 'comment') {
251
- if (rule.comment.trim() === EXPECTED_END_TOKEN) {
252
- ctx.currentAssertion.expected = css.stringify(
253
- { stylesheet: { rules: ctx.currentExpectedRules }});
254
- delete ctx.currentExpectedRules;
255
- ctx.currentAssertion.passed = (
256
- ctx.currentAssertion.output === ctx.currentAssertion.expected);
257
- return parseEndAssertion;
240
+ ctx.currentOutputRules.push(rule);
241
+ return parseAssertionOutput;
242
+ };
243
+
244
+
245
+ var parseAssertionExpectedStart = function (rule, ctx) {
246
+ if (rule.type === 'comment') {
247
+ var text = rule.comment.trim();
248
+ if (!text) { return parseAssertionExpectedStart; }
249
+ if (text === EXPECTED_START_TOKEN) {
250
+ ctx.currentExpectedRules = [];
251
+ return parseAssertionExpected;
252
+ }
253
+ throw parseError('Unexpected comment "' + text + '"', 'EXPECTED', rule.position);
258
254
  }
259
- }
260
- ctx.currentExpectedRules.push(rule);
261
- return parseAssertionExpected;
262
- };
255
+ throw parseError('Unexpected rule type "' + rule.type + '"', 'EXPECTED', rule.position);
256
+ };
257
+
258
+
259
+ var parseAssertionExpected = function (rule, ctx) {
260
+ if (rule.type === 'comment') {
261
+ if (rule.comment.trim() === EXPECTED_END_TOKEN) {
262
+ ctx.currentAssertion.expected = css.stringify(
263
+ { stylesheet: { rules: ctx.currentExpectedRules }});
264
+ delete ctx.currentExpectedRules;
265
+ ctx.currentAssertion.passed = (
266
+ ctx.currentAssertion.output === ctx.currentAssertion.expected);
267
+ return parseEndAssertion;
268
+ }
269
+ }
270
+ ctx.currentExpectedRules.push(rule);
271
+ return parseAssertionExpected;
272
+ };
273
+
274
+
275
+ var parseEndAssertion = function (rule, ctx) {
276
+ if (rule.type === 'comment') {
277
+ var text = rule.comment.trim();
278
+ if (!text) { return parseEndAssertion; }
279
+ if (text === ASSERT_END_TOKEN) {
280
+ finishCurrentAssertion(ctx);
281
+ return parseAssertion;
282
+ }
283
+ throw parseError('Unexpected comment "' + text + '"', 'END_ASSERT', rule.position);
284
+ }
285
+ throw parseError('Unexpected rule type "' + rule.type + '"', 'END_ASSERT', rule.position);
286
+ };
263
287
 
264
288
 
265
- var parseEndAssertion = function (rule, ctx) {
266
- if (rule.type === 'comment') {
267
- var text = rule.comment.trim();
268
- if (!text) { return parseEndAssertion; }
269
- if (text === ASSERT_END_TOKEN) {
270
- finishCurrentAssertion(ctx);
271
- return parseAssertion;
272
- }
273
- throw parseError('Unexpected comment "' + text + '"', 'END_ASSERT', rule.position);
274
- }
275
- throw parseError('Unexpected rule type "' + rule.type + '"', 'END_ASSERT', rule.position);
289
+ return parseCss();
276
290
  };
277
291
 
278
292
 
293
+
279
294
  var finishCurrentModule = function (ctx) {
280
295
  finishCurrentTest(ctx);
281
296
  if (ctx.currentModule) {