Skip to content

Commit 45c5568

Browse files
committed
Getting jsmime can be testing in browser & nodejs, they shareing the same
copy of test cases. To do this, we need to using the same text-encoding for both nodejs & browser. And in the future, we could easily running jsmime in worker, and add UTF7 support.
1 parent 788c66c commit 45c5568

19 files changed

Lines changed: 350 additions & 43 deletions

.babelrc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"plugins": ["transform-es2015-destructuring"]
3+
}

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
11
dist
22
node_modules
3+
/npm-debug.log
4+
/coverage
5+
/jsmime.*

.tern-project

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"ecmaVersion": 6,
3+
"libs": []
4+
}

README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,23 @@ This code depends on the following ES6 features and Web APIs:
2929
* btoa, atob (found on global Windows or WorkerScopes)
3030
* TextDecoder
3131

32+
Developments
33+
============
34+
```
35+
npm install -g mocha # For running mocha tests
36+
npm install -g http-server # Installing the http-server for firefox test
37+
http-server # At the jsmime root directory
38+
39+
npm install -g gulp # Installing gulp for mocha test with node
40+
41+
gulp test #Running all the tests
42+
npm run mocha -- test\test_header.js #Used for running specific test case
43+
44+
gulp bundle # Creating the dist\jsmime.js that could be able used in browser
45+
gulp coverage # Viewing the running result
46+
gulp watch # Watch the file changes & generating the dist\jsmime.js automatically
47+
```
48+
3249
Versions and API stability
3350
==========================
3451

encodings.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
'use strict'
2+
3+
/*
4+
TODO: GB18030 is not working
5+
*/
6+
7+
module.exports = require('text-encoding')

gulpfile.js

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
'use strict'
2+
3+
const watchify = require('watchify')
4+
const browserify = require('browserify')
5+
const gulp = require('gulp')
6+
const plumber = require('gulp-plumber')
7+
// const uglify = require('gulp-uglify')
8+
const source = require('vinyl-source-stream')
9+
const buffer = require('vinyl-buffer')
10+
const gutil = require('gulp-util')
11+
const sourcemaps = require('gulp-sourcemaps')
12+
const assign = require('lodash.assign')
13+
const browserSync = require('browser-sync')
14+
const istanbul = require('gulp-babel-istanbul') // Code coverage
15+
const mocha = require('gulp-mocha') // Unit testing
16+
const rename = require('gulp-rename')
17+
require('./test')
18+
19+
const customOpts = {
20+
entries: ['./jsmimeMain.js'],
21+
debug: true,
22+
transform: [['babelify', { ignore: ['./libs/**'] }]],
23+
ignore: ['./libs/**'],
24+
standalone: 'jsmime'
25+
}
26+
27+
const opts = assign({}, watchify.args, customOpts)
28+
29+
/**
30+
* This task will bundle all other js files and babelify them.
31+
* If you want to add other processing to the main js files, add your code here.
32+
*/
33+
gulp.task('bundle', function () {
34+
const b = browserify(customOpts)
35+
b.on('log', gutil.log)
36+
return b.bundle()
37+
.on('error', function (err) {
38+
console.log(err.message)
39+
browserSync.notify(err.message, 3000)
40+
this.emit('end')
41+
})
42+
.pipe(plumber())
43+
.pipe(source('./jsmimeMain.js'))
44+
.pipe(rename('jsmime.js'))
45+
.pipe(buffer())
46+
// .pipe(uglify())
47+
.pipe(sourcemaps.init({ loadMaps: true }))
48+
.pipe(sourcemaps.write('./'))
49+
.pipe(gulp.dest('./'))
50+
})
51+
52+
/**
53+
* This task would bundle js files and also watching them
54+
*/
55+
const wb = watchify(browserify(opts))
56+
wb.on('log', gutil.log)
57+
gulp.task('watch-bundle', function () {
58+
return wb.bundle()
59+
.on('error', function (err) {
60+
console.log(err.message)
61+
browserSync.notify(err.message, 3000)
62+
this.emit('end')
63+
})
64+
.pipe(plumber())
65+
.pipe(source('./jsmimeMain.js'))
66+
.pipe(rename('jsmime.js'))
67+
.pipe(buffer())
68+
// .pipe(uglify())
69+
.pipe(sourcemaps.init({ loadMaps: true }))
70+
.pipe(sourcemaps.write('./'))
71+
.pipe(gulp.dest('./'))
72+
})
73+
74+
/**
75+
* This task starts watching the files inside 'src'. If a file is changed,
76+
* removed or added then it will run refresh task which will run the bundle task
77+
* and then refresh the page.
78+
*
79+
* For large projects, it may be beneficial to separate copying of libs and
80+
* media from bundling the source. This is especially true if you have large
81+
* amounts of media.
82+
*/
83+
gulp.task('watch', ['watch-bundle'], function () {
84+
var watcher = gulp.watch('./*.js', ['refresh'])
85+
watcher.on('change', function (event) {
86+
console.log('File ' + event.path + ' was ' + event.type + ', running tasks...')
87+
})
88+
})
89+
90+
/**
91+
* This task starts browserSync. Allowing refreshes to be called from the gulp
92+
* bundle task.
93+
*/
94+
gulp.task('browser-sync', ['watch'], function () {
95+
return browserSync({ server: { baseDir: './dist' } })
96+
})
97+
98+
/**
99+
* This is the default task which chains the rest.
100+
*/
101+
gulp.task('default', ['browser-sync'])
102+
103+
/**
104+
* Using a dependency ensures that the bundle task is finished before reloading.
105+
*/
106+
gulp.task('refresh', ['bundle'], browserSync.reload)
107+
108+
function handleError (e) {
109+
console.error(e.toString())
110+
this.emit('end')
111+
}
112+
113+
gulp.task('pre-test', () => {
114+
return gulp.src('*.js')
115+
// Covering files
116+
.pipe(istanbul())
117+
// Force `require` to return covered files
118+
.pipe(istanbul.hookRequire())
119+
})
120+
121+
gulp.task('test', () => {
122+
// gulp-mocha needs filepaths so you can't have any plugins before it
123+
return gulp.src(['test/**/test*.js'], {read: false})
124+
.pipe(
125+
mocha({
126+
ui: 'tdd',
127+
reporter: 'spec',
128+
globals: ['define']
129+
}).on('error', handleError)
130+
)
131+
})
132+
133+
gulp.task('coverage', ['pre-test'], () => {
134+
// gulp-mocha needs filepaths so you can't have any plugins before it
135+
return gulp.src(['test/**/test*.js'], {read: false})
136+
.pipe(
137+
mocha({
138+
ui: 'tdd',
139+
reporter: 'spec',
140+
globals: ['define']
141+
}).on('error', handleError)
142+
)
143+
// Creating the reports after tests ran
144+
.pipe(
145+
istanbul.writeReports()
146+
)
147+
// Enforce a coverage of at least 90%
148+
.pipe(
149+
istanbul.enforceThresholds({ thresholds: { global: 10 } })
150+
)
151+
})

headeremitter.js

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
define(function(require) {
21
/**
32
* This module implements the code for emitting structured representations of
43
* MIME headers into their encoded forms. The code here is a companion to,
@@ -10,6 +9,7 @@ define(function(require) {
109
"use strict";
1110

1211
var mimeutils = require('./mimeutils');
12+
const { TextEncoder } = require('./encodings');
1313

1414
// Get the default structured encoders and add them to the map
1515
var structuredHeaders = require('./structuredHeaders');
@@ -359,7 +359,8 @@ let nonAsciiRe = /[^\x20-\x7e]/;
359359
const b64Prelude = "=?UTF-8?B?", qpPrelude = "=?UTF-8?Q?";
360360

361361
/// A list of ASCII characters forbidden in RFC 2047 encoded-words
362-
const qpForbidden = "=?_()\",";
362+
const qpForbidden = "=?_()\",".split('').map((x) => x.charCodeAt(0))
363+
const spaceCharCode = ' '.charCodeAt(0)
363364

364365
const hexString = "0123456789abcdef";
365366

@@ -376,23 +377,22 @@ const hexString = "0123456789abcdef";
376377
*/
377378
HeaderEmitter.prototype._addRFC2047Word = function (encodedText, useQP,
378379
mayBreakAfter) {
379-
let binaryString = mimeutils.typedArrayToString(encodedText);
380380
if (useQP) {
381381
var token = qpPrelude;
382382
for (let i = 0; i < encodedText.length; i++) {
383383
if (encodedText[i] < 0x20 || encodedText[i] >= 0x7F ||
384-
qpForbidden.includes(binaryString[i])) {
384+
qpForbidden.indexOf(encodedText[i]) >= 0) {
385385
let ch = encodedText[i];
386386
token += "=" + hexString[(ch & 0xf0) >> 4] + hexString[ch & 0x0f];
387-
} else if (binaryString[i] == " ") {
387+
} else if (encodedText[i] === spaceCharCode) {
388388
token += "_";
389389
} else {
390-
token += binaryString[i];
390+
token += String.fromCharCode(encodedText[i]);
391391
}
392392
}
393393
token += "?=";
394394
} else {
395-
var token = b64Prelude + btoa(binaryString) + "?=";
395+
var token = b64Prelude + btoa(encodedText) + "?=";
396396
}
397397
this.addText(token, mayBreakAfter);
398398
};
@@ -427,7 +427,7 @@ HeaderEmitter.prototype.encodeRFC2047Phrase = function (text, mayBreakAfter) {
427427

428428
// The length for quoted-printable is 3 chars only if encoded
429429
if (encodedText[i] < 0x20 || encodedText[i] >= 0x7f ||
430-
qpForbidden.includes(String.fromCharCode(encodedText[i]))) {
430+
qpForbidden.indexOf(encodedText[i]) >= 0) {
431431
qpInc = 3;
432432
} else {
433433
qpInc = 1;
@@ -768,12 +768,9 @@ function addStructuredEncoder(header, encoder) {
768768
preferredSpellings.set(lowerName, header);
769769
}
770770

771-
return Object.freeze({
771+
module.exports = Object.freeze({
772772
addStructuredEncoder: addStructuredEncoder,
773773
emitStructuredHeader: emitStructuredHeader,
774774
emitStructuredHeaders: emitStructuredHeaders,
775775
makeStreamingEmitter: makeStreamingEmitter
776776
});
777-
778-
});
779-

headerparser.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
define(function(require) {
21
/**
32
* This file implements the structured decoding of message header fields. It is
43
* part of the same system as found in mimemimeutils.js, and occasionally makes
@@ -8,6 +7,7 @@ define(function(require) {
87

98
"use strict";
109
var mimeutils = require('./mimeutils');
10+
const { TextDecoder } = require('./encodings');
1111

1212
/**
1313
* This is the API that we ultimately return.
@@ -934,7 +934,10 @@ const kKnownTZs = {
934934
* above.
935935
*/
936936
function parseDateHeader(header) {
937-
let tokens = [for (x of getHeaderTokens(header, ",:", {})) x.toString()];
937+
let tokens = []
938+
for (let x of getHeaderTokens(header, ",:", {})) {
939+
tokens.push(x.toString());
940+
}
938941
// What does a Date header look like? In practice, most date headers devolve
939942
// into Date: [dow ,] dom mon year hh:mm:ss tzoff [(abbrev)], with the day of
940943
// week mostly present and the timezone abbreviation mostly absent.
@@ -1148,7 +1151,4 @@ headerparser.parseAddressingHeader = parseAddressingHeader;
11481151
headerparser.parseDateHeader = parseDateHeader;
11491152
headerparser.parseParameterHeader = parseParameterHeader;
11501153
headerparser.parseStructuredHeader = parseStructuredHeader;
1151-
return Object.freeze(headerparser);
1152-
1153-
});
1154-
1154+
module.exports = Object.freeze(headerparser);

jsmime.js

Lines changed: 0 additions & 7 deletions
This file was deleted.

jsmimeMain.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module.exports = {
2+
MimeParser: require('./mimeparser'),
3+
headerparser: require('./headerparser'),
4+
headeremitter: require('./headeremitter')
5+
}

0 commit comments

Comments
 (0)