1
0
mirror of https://github.com/laurent22/joplin.git synced 2026-03-09 09:47:34 +02:00

Compare commits

...

3 Commits

19 changed files with 307 additions and 250 deletions

View File

@@ -910,6 +910,9 @@ packages/lib/errorUtils.js.map
packages/lib/eventManager.d.ts
packages/lib/eventManager.js
packages/lib/eventManager.js.map
packages/lib/fs-driver-base.d.ts
packages/lib/fs-driver-base.js
packages/lib/fs-driver-base.js.map
packages/lib/fs-driver-node.d.ts
packages/lib/fs-driver-node.js
packages/lib/fs-driver-node.js.map

3
.gitignore vendored
View File

@@ -899,6 +899,9 @@ packages/lib/errorUtils.js.map
packages/lib/eventManager.d.ts
packages/lib/eventManager.js
packages/lib/eventManager.js.map
packages/lib/fs-driver-base.d.ts
packages/lib/fs-driver-base.js
packages/lib/fs-driver-base.js.map
packages/lib/fs-driver-node.d.ts
packages/lib/fs-driver-node.js
packages/lib/fs-driver-node.js.map

View File

@@ -54,4 +54,5 @@ module.exports = {
testEnvironment: 'node',
setupFilesAfterEnv: [`${__dirname}/jest.setup.js`],
slowTestThreshold: 20,
};

View File

@@ -1,2 +1,2 @@
data/
test data/
export/

View File

@@ -1,97 +1,83 @@
/* eslint-disable no-unused-vars */
const os = require('os');
const time = require('@joplin/lib/time').default;
const { filename } = require('@joplin/lib/path-utils');
const { fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('./test-utils.js');
const Folder = require('@joplin/lib/models/Folder.js');
const Note = require('@joplin/lib/models/Note.js');
const BaseModel = require('@joplin/lib/BaseModel').default;
const shim = require('@joplin/lib/shim').default;
const HtmlToMd = require('@joplin/lib/HtmlToMd');
const { enexXmlToMd } = require('@joplin/lib/import-enex-md-gen.js');
describe('HtmlToMd', function() {
beforeEach(async (done) => {
await setupDatabaseAndSynchronizer(1);
await switchClient(1);
done();
});
it('should convert from Html to Markdown', (async () => {
const basePath = `${__dirname}/html_to_md`;
const files = await shim.fsDriver().readDirStats(basePath);
const htmlToMd = new HtmlToMd();
for (let i = 0; i < files.length; i++) {
const htmlFilename = files[i].path;
if (htmlFilename.indexOf('.html') < 0) continue;
const htmlPath = `${basePath}/${htmlFilename}`;
const mdPath = `${basePath}/${filename(htmlFilename)}.md`;
// if (htmlFilename !== 'code_3.html') continue;
// if (htmlFilename.indexOf('image_preserve_size') !== 0) continue;
const htmlToMdOptions = {};
if (htmlFilename === 'anchor_local.html') {
// Normally the list of anchor names in the document are retrieved from the HTML code
// This is straightforward when the document is still in DOM format, as with the clipper,
// but otherwise it would need to be somehow parsed out from the HTML. Here we just
// hard code the anchors that we know are in the file.
htmlToMdOptions.anchorNames = ['first', 'second', 'fourth'];
}
if (htmlFilename.indexOf('image_preserve_size') === 0) {
htmlToMdOptions.preserveImageTagsWithSize = true;
}
const html = await shim.fsDriver().readFile(htmlPath);
let expectedMd = await shim.fsDriver().readFile(mdPath);
let actualMd = await htmlToMd.parse(`<div>${html}</div>`, htmlToMdOptions);
if (os.EOL === '\r\n') {
expectedMd = expectedMd.replace(/\r\n/g, '\n');
actualMd = actualMd.replace(/\r\n/g, '\n');
}
if (actualMd !== expectedMd) {
const result = [];
result.push('');
result.push(`Error converting file: ${htmlFilename}`);
result.push('--------------------------------- Got:');
result.push(actualMd.split('\n').map(l => `"${l}"`).join('\n'));
result.push('--------------------------------- Expected:');
result.push(expectedMd.split('\n').map(l => `"${l}"`).join('\n'));
result.push('--------------------------------------------');
result.push('');
console.info(result.join('\n'));
// console.info('');
// console.info(`Error converting file: ${htmlFilename}`);
// console.info('--------------------------------- Got:');
// console.info(actualMd);
// console.info('--------------------------------- Raw:');
// console.info(actualMd.split('\n'));
// console.info('--------------------------------- Expected:');
// console.info(expectedMd.split('\n'));
// console.info('--------------------------------------------');
// console.info('');
expect(false).toBe(true);
// return;
} else {
expect(true).toBe(true);
}
}
}));
});
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const shim_1 = require("@joplin/lib/shim");
const os = require('os');
const { filename } = require('@joplin/lib/path-utils');
const HtmlToMd = require('@joplin/lib/HtmlToMd');
describe('HtmlToMd', function () {
// beforeEach(async (done) => {
// await setupDatabaseAndSynchronizer(1);
// await switchClient(1);
// done();
// });
it('should convert from Html to Markdown', (() => __awaiter(this, void 0, void 0, function* () {
const basePath = `${__dirname}/html_to_md`;
const files = yield shim_1.default.fsDriver().readDirStats(basePath);
const htmlToMd = new HtmlToMd();
for (let i = 0; i < files.length; i++) {
const htmlFilename = files[i].path;
if (htmlFilename.indexOf('.html') < 0)
continue;
const htmlPath = `${basePath}/${htmlFilename}`;
const mdPath = `${basePath}/${filename(htmlFilename)}.md`;
if (htmlFilename !== 'anchor_same_title_and_url.html')
continue;
// if (htmlFilename.indexOf('image_preserve_size') !== 0) continue;
const htmlToMdOptions = {};
if (htmlFilename === 'anchor_local.html') {
// Normally the list of anchor names in the document are retrieved from the HTML code
// This is straightforward when the document is still in DOM format, as with the clipper,
// but otherwise it would need to be somehow parsed out from the HTML. Here we just
// hard code the anchors that we know are in the file.
htmlToMdOptions.anchorNames = ['first', 'second', 'fourth'];
}
if (htmlFilename.indexOf('image_preserve_size') === 0) {
htmlToMdOptions.preserveImageTagsWithSize = true;
}
const html = yield shim_1.default.fsDriver().readFile(htmlPath);
let expectedMd = yield shim_1.default.fsDriver().readFile(mdPath);
let actualMd = yield htmlToMd.parse(`<div>${html}</div>`, htmlToMdOptions);
if (os.EOL === '\r\n') {
expectedMd = expectedMd.replace(/\r\n/g, '\n');
actualMd = actualMd.replace(/\r\n/g, '\n');
}
if (actualMd !== expectedMd) {
const result = [];
result.push('');
result.push(`Error converting file: ${htmlFilename}`);
result.push('--------------------------------- Got:');
result.push(actualMd.split('\n').map(l => `"${l}"`).join('\n'));
result.push('--------------------------------- Expected:');
result.push(expectedMd.split('\n').map(l => `"${l}"`).join('\n'));
result.push('--------------------------------------------');
result.push('');
console.info(result.join('\n'));
// console.info('');
// console.info(`Error converting file: ${htmlFilename}`);
// console.info('--------------------------------- Got:');
// console.info(actualMd);
// console.info('--------------------------------- Raw:');
// console.info(actualMd.split('\n'));
// console.info('--------------------------------- Expected:');
// console.info(expectedMd.split('\n'));
// console.info('--------------------------------------------');
// console.info('');
expect(false).toBe(true);
// return;
}
else {
expect(true).toBe(true);
}
}
})));
});
//# sourceMappingURL=HtmlToMd.js.map

View File

@@ -0,0 +1,87 @@
import shim from '@joplin/lib/shim';
const os = require('os');
const { filename } = require('@joplin/lib/path-utils');
const HtmlToMd = require('@joplin/lib/HtmlToMd');
describe('HtmlToMd', function() {
// beforeEach(async (done) => {
// await setupDatabaseAndSynchronizer(1);
// await switchClient(1);
// done();
// });
it('should convert from Html to Markdown', (async () => {
const basePath = `${__dirname}/html_to_md`;
const files = await shim.fsDriver().readDirStats(basePath);
const htmlToMd = new HtmlToMd();
for (let i = 0; i < files.length; i++) {
const htmlFilename = files[i].path;
if (htmlFilename.indexOf('.html') < 0) continue;
const htmlPath = `${basePath}/${htmlFilename}`;
const mdPath = `${basePath}/${filename(htmlFilename)}.md`;
if (htmlFilename !== 'anchor_same_title_and_url.html') continue;
// if (htmlFilename.indexOf('image_preserve_size') !== 0) continue;
const htmlToMdOptions:any = {};
if (htmlFilename === 'anchor_local.html') {
// Normally the list of anchor names in the document are retrieved from the HTML code
// This is straightforward when the document is still in DOM format, as with the clipper,
// but otherwise it would need to be somehow parsed out from the HTML. Here we just
// hard code the anchors that we know are in the file.
htmlToMdOptions.anchorNames = ['first', 'second', 'fourth'];
}
if (htmlFilename.indexOf('image_preserve_size') === 0) {
htmlToMdOptions.preserveImageTagsWithSize = true;
}
const html = await shim.fsDriver().readFile(htmlPath);
let expectedMd = await shim.fsDriver().readFile(mdPath);
let actualMd = await htmlToMd.parse(`<div>${html}</div>`, htmlToMdOptions);
if (os.EOL === '\r\n') {
expectedMd = expectedMd.replace(/\r\n/g, '\n');
actualMd = actualMd.replace(/\r\n/g, '\n');
}
if (actualMd !== expectedMd) {
const result = [];
result.push('');
result.push(`Error converting file: ${htmlFilename}`);
result.push('--------------------------------- Got:');
result.push(actualMd.split('\n').map(l => `"${l}"`).join('\n'));
result.push('--------------------------------- Expected:');
result.push(expectedMd.split('\n').map(l => `"${l}"`).join('\n'));
result.push('--------------------------------------------');
result.push('');
console.info(result.join('\n'));
// console.info('');
// console.info(`Error converting file: ${htmlFilename}`);
// console.info('--------------------------------- Got:');
// console.info(actualMd);
// console.info('--------------------------------- Raw:');
// console.info(actualMd.split('\n'));
// console.info('--------------------------------- Expected:');
// console.info(expectedMd.split('\n'));
// console.info('--------------------------------------------');
// console.info('');
expect(false).toBe(true);
// return;
} else {
expect(true).toBe(true);
}
}
}));
});

View File

@@ -0,0 +1 @@
<a href="https://example.com"/>https://example.com</a>

View File

@@ -0,0 +1 @@
https://example.com

View File

@@ -1,6 +1,7 @@
import Setting from '@joplin/lib/models/Setting';
import BaseModel from '@joplin/lib/BaseModel';
import shim from '@joplin/lib/shim';
import markdownUtils from '@joplin/lib/markdownUtils';
const { sortedIds, createNTestNotes, setupDatabaseAndSynchronizer, switchClient, checkThrowAsync } = require('./test-utils.js');
const Folder = require('@joplin/lib/models/Folder.js');
const Note = require('@joplin/lib/models/Note.js');
@@ -217,6 +218,8 @@ describe('models_Note', function() {
const t1 = r1.updated_time;
const t2 = r2.updated_time;
const resourceDirE = markdownUtils.escapeLinkUrl(resourceDir);
const testCases = [
[
false,
@@ -241,17 +244,17 @@ describe('models_Note', function() {
[
true,
`![](:/${r1.id})`,
`![](file://${resourceDir}/${r1.id}.jpg?t=${t1})`,
`![](file://${resourceDirE}/${r1.id}.jpg?t=${t1})`,
],
[
true,
`![](:/${r1.id}) ![](:/${r1.id}) ![](:/${r2.id})`,
`![](file://${resourceDir}/${r1.id}.jpg?t=${t1}) ![](file://${resourceDir}/${r1.id}.jpg?t=${t1}) ![](file://${resourceDir}/${r2.id}.jpg?t=${t2})`,
`![](file://${resourceDirE}/${r1.id}.jpg?t=${t1}) ![](file://${resourceDirE}/${r1.id}.jpg?t=${t1}) ![](file://${resourceDirE}/${r2.id}.jpg?t=${t2})`,
],
[
true,
`![](:/${r3.id})`,
`![](file://${resourceDir}/${r3.id}.pdf)`,
`![](file://${resourceDirE}/${r3.id}.pdf)`,
],
];

View File

@@ -2,8 +2,7 @@ import InteropService from '@joplin/lib/services/interop/InteropService';
import { CustomExportContext, CustomImportContext, Module, ModuleType } from '@joplin/lib/services/interop/types';
import shim from '@joplin/lib/shim';
const { fileContentEqual, setupDatabaseAndSynchronizer, switchClient, checkThrowAsync } = require('./test-utils.js');
const { fileContentEqual, setupDatabaseAndSynchronizer, switchClient, checkThrowAsync, exportDir } = require('./test-utils.js');
const Folder = require('@joplin/lib/models/Folder.js');
const Note = require('@joplin/lib/models/Note.js');
const Tag = require('@joplin/lib/models/Tag.js');
@@ -11,10 +10,6 @@ const Resource = require('@joplin/lib/models/Resource.js');
const fs = require('fs-extra');
const ArrayUtils = require('@joplin/lib/ArrayUtils');
function exportDir() {
return `${__dirname}/export`;
}
async function recreateExportDir() {
const dir = exportDir();
await fs.remove(dir);

View File

@@ -2,7 +2,7 @@
const fs = require('fs-extra');
const { setupDatabaseAndSynchronizer, switchClient } = require('./test-utils.js');
const { setupDatabaseAndSynchronizer, switchClient, exportDir } = require('./test-utils.js');
const InteropService_Exporter_Md = require('@joplin/lib/services/interop/InteropService_Exporter_Md').default;
const BaseModel = require('@joplin/lib/BaseModel').default;
const Folder = require('@joplin/lib/models/Folder.js');
@@ -10,29 +10,27 @@ const Resource = require('@joplin/lib/models/Resource.js');
const Note = require('@joplin/lib/models/Note.js');
const shim = require('@joplin/lib/shim').default;
const exportDir = `${__dirname}/export`;
describe('services_InteropService_Exporter_Md', function() {
beforeEach(async (done) => {
await setupDatabaseAndSynchronizer(1);
await switchClient(1);
await fs.remove(exportDir);
await fs.mkdirp(exportDir);
await fs.remove(exportDir());
await fs.mkdirp(exportDir());
done();
});
it('should create resources directory', (async () => {
const service = new InteropService_Exporter_Md();
await service.init(exportDir);
await service.init(exportDir());
expect(await shim.fsDriver().exists(`${exportDir}/_resources/`)).toBe(true);
expect(await shim.fsDriver().exists(`${exportDir()}/_resources/`)).toBe(true);
}));
it('should create note paths and add them to context', (async () => {
const exporter = new InteropService_Exporter_Md();
await exporter.init(exportDir);
await exporter.init(exportDir());
const itemsToExport = [];
const queueExportItem = (itemType, itemOrId) => {
@@ -74,7 +72,7 @@ describe('services_InteropService_Exporter_Md', function() {
it('should handle duplicate note names', (async () => {
const exporter = new InteropService_Exporter_Md();
await exporter.init(exportDir);
await exporter.init(exportDir());
const itemsToExport = [];
const queueExportItem = (itemType, itemOrId) => {
@@ -101,7 +99,7 @@ describe('services_InteropService_Exporter_Md', function() {
it('should not override existing files', (async () => {
const exporter = new InteropService_Exporter_Md();
await exporter.init(exportDir);
await exporter.init(exportDir());
const itemsToExport = [];
const queueExportItem = (itemType, itemOrId) => {
@@ -118,7 +116,7 @@ describe('services_InteropService_Exporter_Md', function() {
await exporter.processItem(Folder.modelType(), folder1);
// Create a file with the path of note1 before processing note1
await shim.fsDriver().writeFile(`${exportDir}/folder1/note1.md`, 'Note content', 'utf-8');
await shim.fsDriver().writeFile(`${exportDir()}/folder1/note1.md`, 'Note content', 'utf-8');
await exporter.prepareForProcessingItemType(BaseModel.TYPE_NOTE, itemsToExport);
@@ -128,7 +126,7 @@ describe('services_InteropService_Exporter_Md', function() {
it('should save resource files in _resource directory', (async () => {
const exporter = new InteropService_Exporter_Md();
await exporter.init(exportDir);
await exporter.init(exportDir());
const itemsToExport = [];
const queueExportItem = (itemType, itemOrId) => {
@@ -159,13 +157,13 @@ describe('services_InteropService_Exporter_Md', function() {
await exporter.processResource(resource1, Resource.fullPath(resource1));
await exporter.processResource(resource2, Resource.fullPath(resource2));
expect(await shim.fsDriver().exists(`${exportDir}/_resources/${Resource.filename(resource1)}`)).toBe(true, 'Resource file should be copied to _resources directory.');
expect(await shim.fsDriver().exists(`${exportDir}/_resources/${Resource.filename(resource2)}`)).toBe(true, 'Resource file should be copied to _resources directory.');
expect(await shim.fsDriver().exists(`${exportDir()}/_resources/${Resource.filename(resource1)}`)).toBe(true, 'Resource file should be copied to _resources directory.');
expect(await shim.fsDriver().exists(`${exportDir()}/_resources/${Resource.filename(resource2)}`)).toBe(true, 'Resource file should be copied to _resources directory.');
}));
it('should create folders in fs', (async () => {
const exporter = new InteropService_Exporter_Md();
await exporter.init(exportDir);
await exporter.init(exportDir());
const itemsToExport = [];
const queueExportItem = (itemType, itemOrId) => {
@@ -189,14 +187,14 @@ describe('services_InteropService_Exporter_Md', function() {
await exporter.prepareForProcessingItemType(BaseModel.TYPE_NOTE, itemsToExport);
await exporter.processItem(Note.modelType(), note2);
expect(await shim.fsDriver().exists(`${exportDir}/folder1`)).toBe(true, 'Folder should be created in filesystem.');
expect(await shim.fsDriver().exists(`${exportDir}/folder1/folder2`)).toBe(true, 'Folder should be created in filesystem.');
expect(await shim.fsDriver().exists(`${exportDir}/folder1/folder3`)).toBe(true, 'Folder should be created in filesystem.');
expect(await shim.fsDriver().exists(`${exportDir()}/folder1`)).toBe(true, 'Folder should be created in filesystem.');
expect(await shim.fsDriver().exists(`${exportDir()}/folder1/folder2`)).toBe(true, 'Folder should be created in filesystem.');
expect(await shim.fsDriver().exists(`${exportDir()}/folder1/folder3`)).toBe(true, 'Folder should be created in filesystem.');
}));
it('should save notes in fs', (async () => {
const exporter = new InteropService_Exporter_Md();
await exporter.init(exportDir);
await exporter.init(exportDir());
const itemsToExport = [];
const queueExportItem = (itemType, itemOrId) => {
@@ -226,14 +224,14 @@ describe('services_InteropService_Exporter_Md', function() {
await exporter.processItem(Note.modelType(), note2);
await exporter.processItem(Note.modelType(), note3);
expect(await shim.fsDriver().exists(`${exportDir}/${exporter.context().notePaths[note1.id]}`)).toBe(true, 'File should be saved in filesystem.');
expect(await shim.fsDriver().exists(`${exportDir}/${exporter.context().notePaths[note2.id]}`)).toBe(true, 'File should be saved in filesystem.');
expect(await shim.fsDriver().exists(`${exportDir}/${exporter.context().notePaths[note3.id]}`)).toBe(true, 'File should be saved in filesystem.');
expect(await shim.fsDriver().exists(`${exportDir()}/${exporter.context().notePaths[note1.id]}`)).toBe(true, 'File should be saved in filesystem.');
expect(await shim.fsDriver().exists(`${exportDir()}/${exporter.context().notePaths[note2.id]}`)).toBe(true, 'File should be saved in filesystem.');
expect(await shim.fsDriver().exists(`${exportDir()}/${exporter.context().notePaths[note3.id]}`)).toBe(true, 'File should be saved in filesystem.');
}));
it('should replace resource ids with relative paths', (async () => {
const exporter = new InteropService_Exporter_Md();
await exporter.init(exportDir);
await exporter.init(exportDir());
const itemsToExport = [];
const queueExportItem = (itemType, itemOrId) => {
@@ -269,8 +267,8 @@ describe('services_InteropService_Exporter_Md', function() {
await exporter.processItem(Note.modelType(), note1);
await exporter.processItem(Note.modelType(), note2);
const note1_body = await shim.fsDriver().readFile(`${exportDir}/${exporter.context().notePaths[note1.id]}`);
const note2_body = await shim.fsDriver().readFile(`${exportDir}/${exporter.context().notePaths[note2.id]}`);
const note1_body = await shim.fsDriver().readFile(`${exportDir()}/${exporter.context().notePaths[note1.id]}`);
const note2_body = await shim.fsDriver().readFile(`${exportDir()}/${exporter.context().notePaths[note2.id]}`);
expect(note1_body).toContain('](../_resources/resource1.jpg)', 'Resource id should be replaced with a relative path.');
expect(note2_body).toContain('](../../_resources/resource2.jpg)', 'Resource id should be replaced with a relative path.');
@@ -278,7 +276,7 @@ describe('services_InteropService_Exporter_Md', function() {
it('should replace note ids with relative paths', (async () => {
const exporter = new InteropService_Exporter_Md();
await exporter.init(exportDir);
await exporter.init(exportDir());
const itemsToExport = [];
const queueExportItem = (itemType, itemOrId) => {
@@ -318,9 +316,9 @@ describe('services_InteropService_Exporter_Md', function() {
await exporter.processItem(Note.modelType(), note2);
await exporter.processItem(Note.modelType(), note3);
const note1_body = await shim.fsDriver().readFile(`${exportDir}/${exporter.context().notePaths[note1.id]}`);
const note2_body = await shim.fsDriver().readFile(`${exportDir}/${exporter.context().notePaths[note2.id]}`);
const note3_body = await shim.fsDriver().readFile(`${exportDir}/${exporter.context().notePaths[note3.id]}`);
const note1_body = await shim.fsDriver().readFile(`${exportDir()}/${exporter.context().notePaths[note1.id]}`);
const note2_body = await shim.fsDriver().readFile(`${exportDir()}/${exporter.context().notePaths[note2.id]}`);
const note3_body = await shim.fsDriver().readFile(`${exportDir()}/${exporter.context().notePaths[note3.id]}`);
expect(note1_body).toContain('](../folder3/note3.md)', 'Note id should be replaced with a relative path.');
expect(note2_body).toContain('](../../folder3/note3.md)', 'Resource id should be replaced with a relative path.');
@@ -330,7 +328,7 @@ describe('services_InteropService_Exporter_Md', function() {
it('should url encode relative note links', (async () => {
const exporter = new InteropService_Exporter_Md();
await exporter.init(exportDir);
await exporter.init(exportDir());
const itemsToExport = [];
const queueExportItem = (itemType, itemOrId) => {
@@ -351,7 +349,7 @@ describe('services_InteropService_Exporter_Md', function() {
await exporter.processItem(Note.modelType(), note1);
await exporter.processItem(Note.modelType(), note2);
const note2_body = await shim.fsDriver().readFile(`${exportDir}/${exporter.context().notePaths[note2.id]}`);
const note2_body = await shim.fsDriver().readFile(`${exportDir()}/${exporter.context().notePaths[note2.id]}`);
expect(note2_body).toContain('[link](../folder%20with%20space1/note1%20name%20with%20space.md)', 'Whitespace in URL should be encoded');
}));
});

View File

@@ -1,86 +0,0 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.localNotesFoldersSameAsRemote = exports.remoteResources = exports.remoteNotesFoldersResources = exports.remoteNotesAndFolders = exports.allNotesFolders = void 0;
const BaseModel_1 = require("@joplin/lib/BaseModel");
const { fileApi } = require('./test-utils.js');
const Folder = require('@joplin/lib/models/Folder.js');
const Note = require('@joplin/lib/models/Note.js');
const BaseItem = require('@joplin/lib/models/BaseItem.js');
function allNotesFolders() {
return __awaiter(this, void 0, void 0, function* () {
const folders = yield Folder.all();
const notes = yield Note.all();
return folders.concat(notes);
});
}
exports.allNotesFolders = allNotesFolders;
function remoteItemsByTypes(types) {
return __awaiter(this, void 0, void 0, function* () {
const list = yield fileApi().list('', { includeDirs: false, syncItemsOnly: true });
if (list.has_more)
throw new Error('Not implemented!!!');
const files = list.items;
const output = [];
for (const file of files) {
const remoteContent = yield fileApi().get(file.path);
const content = yield BaseItem.unserialize(remoteContent);
if (types.indexOf(content.type_) < 0)
continue;
output.push(content);
}
return output;
});
}
function remoteNotesAndFolders() {
return __awaiter(this, void 0, void 0, function* () {
return remoteItemsByTypes([BaseModel_1.default.TYPE_NOTE, BaseModel_1.default.TYPE_FOLDER]);
});
}
exports.remoteNotesAndFolders = remoteNotesAndFolders;
function remoteNotesFoldersResources() {
return __awaiter(this, void 0, void 0, function* () {
return remoteItemsByTypes([BaseModel_1.default.TYPE_NOTE, BaseModel_1.default.TYPE_FOLDER, BaseModel_1.default.TYPE_RESOURCE]);
});
}
exports.remoteNotesFoldersResources = remoteNotesFoldersResources;
function remoteResources() {
return __awaiter(this, void 0, void 0, function* () {
return remoteItemsByTypes([BaseModel_1.default.TYPE_RESOURCE]);
});
}
exports.remoteResources = remoteResources;
function localNotesFoldersSameAsRemote(locals, expect) {
return __awaiter(this, void 0, void 0, function* () {
let error = null;
try {
const nf = yield remoteNotesAndFolders();
expect(locals.length).toBe(nf.length);
for (let i = 0; i < locals.length; i++) {
const dbItem = locals[i];
const path = BaseItem.systemPath(dbItem);
const remote = yield fileApi().stat(path);
expect(!!remote).toBe(true);
if (!remote)
continue;
let remoteContent = yield fileApi().get(path);
remoteContent = dbItem.type_ == BaseModel_1.default.TYPE_NOTE ? yield Note.unserialize(remoteContent) : yield Folder.unserialize(remoteContent);
expect(remoteContent.title).toBe(dbItem.title);
}
}
catch (e) {
error = e;
}
expect(error).toBe(null);
});
}
exports.localNotesFoldersSameAsRemote = localNotesFoldersSameAsRemote;
//# sourceMappingURL=test-utils-synchronizer.js.map

View File

@@ -100,7 +100,11 @@ FileApiDriverLocal.fsDriver_ = fsDriver;
const logDir = `${__dirname}/../tests/logs`;
const baseTempDir = `${__dirname}/../tests/tmp/${suiteName_}`;
const dataDir = `${__dirname}/data/${suiteName_}`;
// We add a space in the data directory path as that will help uncover
// various space-in-path issues.
const dataDir = `${__dirname}/test data/${suiteName_}`;
fs.mkdirpSync(logDir, 0o755);
fs.mkdirpSync(baseTempDir, 0o755);
fs.mkdirpSync(dataDir);
@@ -302,6 +306,11 @@ async function setupDatabase(id: number = null, options: any = null) {
await loadKeychainServiceAndSettings(options.keychainEnabled ? KeychainServiceDriver : KeychainServiceDriverDummy);
}
function exportDir(id: number = null) {
if (id === null) id = currentClient_;
return `${dataDir}/export`;
}
function resourceDirName(id: number = null) {
if (id === null) id = currentClient_;
return `resources-${id}`;
@@ -783,4 +792,4 @@ class TestApp extends BaseApplication {
}
}
module.exports = { newPluginService, newPluginScript, synchronizerStart, afterEachCleanUp, syncTargetName, setSyncTargetName, syncDir, createTempDir, isNetworkSyncTarget, kvStore, expectThrow, logger, expectNotThrow, resourceService, resourceFetcher, tempFilePath, allSyncTargetItemsEncrypted, msleep, setupDatabase, revisionService, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync, checkThrow, encryptionService, loadEncryptionMasterKey, fileContentEqual, decryptionWorker, currentClientId, id, ids, sortedIds, at, createNTestNotes, createNTestFolders, createNTestTags, TestApp };
module.exports = { exportDir, newPluginService, newPluginScript, synchronizerStart, afterEachCleanUp, syncTargetName, setSyncTargetName, syncDir, createTempDir, isNetworkSyncTarget, kvStore, expectThrow, logger, expectNotThrow, resourceService, resourceFetcher, tempFilePath, allSyncTargetItemsEncrypted, msleep, setupDatabase, revisionService, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync, checkThrow, encryptionService, loadEncryptionMasterKey, fileContentEqual, decryptionWorker, currentClientId, id, ids, sortedIds, at, createNTestNotes, createNTestFolders, createNTestTags, TestApp };

View File

@@ -1,5 +1,5 @@
const RNFS = require('react-native-fs');
const FsDriverBase = require('@joplin/lib/fs-driver-base');
const FsDriverBase = require('@joplin/lib/fs-driver-base').default;
const RNFetchBlob = require('rn-fetch-blob').default;
class FsDriverRN extends FsDriverBase {

View File

@@ -1,15 +1,48 @@
const { filename, fileExtension } = require('./path-utils');
const time = require('./time').default;
const Setting = require('./models/Setting').default;
import time from './time';
import Setting from './models/Setting';
import { filename, fileExtension } from './path-utils';
const md5 = require('md5');
class FsDriverBase {
async isDirectory(path) {
export interface Stat {
birthtime: number;
mtime: number;
isDirectory(): boolean;
path: string;
size: number;
}
export interface ReadDirStatsOptions {
recursive: boolean;
}
export default class FsDriverBase {
public async stat(_path: string): Promise<Stat> {
throw new Error('Not implemented');
}
public async readDirStats(_path: string, _options: ReadDirStatsOptions = null): Promise<Stat[]> {
throw new Error('Not implemented');
}
public async exists(_path: string): Promise<boolean> {
throw new Error('Not implemented');
}
public async remove(_path: string): Promise<void> {
throw new Error('Not implemented');
}
public async isDirectory(path: string) {
const stat = await this.stat(path);
return !stat ? false : stat.isDirectory();
}
async readDirStatsHandleRecursion_(basePath, stat, output, options) {
public async writeFile(_path: string, _content: string, _encoding: string = 'base64'): Promise<void> {
throw new Error('Not implemented');
}
protected async readDirStatsHandleRecursion_(basePath: string, stat: Stat, output: Stat[], options: ReadDirStatsOptions): Promise<Stat[]> {
if (options.recursive && stat.isDirectory()) {
const subPath = `${basePath}/${stat.path}`;
const subStats = await this.readDirStats(subPath, options);
@@ -23,7 +56,7 @@ class FsDriverBase {
return output;
}
async findUniqueFilename(name, reservedNames = null) {
public async findUniqueFilename(name: string, reservedNames: string[] = null): Promise<string> {
if (reservedNames === null) {
reservedNames = [];
}
@@ -47,7 +80,7 @@ class FsDriverBase {
}
}
async removeAllThatStartWith(dirPath, filenameStart) {
public async removeAllThatStartWith(dirPath: string, filenameStart: string) {
if (!filenameStart || !dirPath) throw new Error('dirPath and filenameStart cannot be empty');
const stats = await this.readDirStats(dirPath);
@@ -59,7 +92,7 @@ class FsDriverBase {
}
}
async waitTillExists(path, timeout = 10000) {
public async waitTillExists(path: string, timeout: number = 10000) {
const startTime = Date.now();
while (true) {
@@ -72,7 +105,7 @@ class FsDriverBase {
// TODO: move out of here and make it part of joplin-renderer
// or assign to option using .bind(fsDriver())
async cacheCssToFile(cssStrings) {
public async cacheCssToFile(cssStrings: string[]) {
const cssString = Array.isArray(cssStrings) ? cssStrings.join('\n') : cssStrings;
const cssFilePath = `${Setting.value('tempDir')}/${md5(escape(cssString))}.css`;
if (!(await this.exists(cssFilePath))) {
@@ -84,6 +117,5 @@ class FsDriverBase {
mime: 'text/css',
};
}
}
module.exports = FsDriverBase;
}

View File

@@ -1,8 +1,8 @@
import { resolve as nodeResolve } from 'path';
import FsDriverBase, { Stat } from './fs-driver-base';
import time from './time';
const fs = require('fs-extra');
const time = require('./time').default;
const FsDriverBase = require('./fs-driver-base');
export default class FsDriverNode extends FsDriverBase {
@@ -125,7 +125,7 @@ export default class FsDriverNode extends FsDriverBase {
throw this.fsErrorToJsError_(error);
}
let output = [];
let output: Stat[] = [];
for (let i = 0; i < items.length; i++) {
const item = items[i];
const stat = await this.stat(`${path}/${item}`);

View File

@@ -171,7 +171,7 @@ class Note extends BaseItem {
useAbsolutePaths: false,
}, options);
const pathsToTry = [];
let pathsToTry = [];
if (options.useAbsolutePaths) {
pathsToTry.push(`file://${Setting.value('resourceDir')}`);
pathsToTry.push(`file://${shim.pathRelativeToCwd(Setting.value('resourceDir'))}`);
@@ -179,6 +179,21 @@ class Note extends BaseItem {
pathsToTry.push(Resource.baseRelativeDirectoryPath());
}
// We support both the escaped and unescaped versions because both
// of those paths are valid:
//
// [](file://C:/I like spaces in paths/abcdefg.jpg)
// [](file://C:/I%20like%20spaces%20in%20paths/abcdefg.jpg)
//
// https://discourse.joplinapp.org/t/12986/4
const temp = [];
for (const p of pathsToTry) {
temp.push(p);
temp.push(markdownUtils.escapeLinkUrl(p));
}
pathsToTry = temp;
this.logger().debug('replaceResourceExternalToInternalLinks', 'options:', options, 'pathsToTry:', pathsToTry);
for (const basePath of pathsToTry) {

View File

@@ -1,11 +1,17 @@
import { _ } from '../../locale';
const InteropService_Exporter_Base = require('./InteropService_Exporter_Base').default;
const InteropService_Exporter_Raw = require('./InteropService_Exporter_Raw').default;
import InteropService_Exporter_Base from './InteropService_Exporter_Base';
import InteropService_Exporter_Raw from './InteropService_Exporter_Raw';
import shim from '../../shim';
const fs = require('fs-extra');
const shim = require('../../shim').default;
export default class InteropService_Exporter_Jex extends InteropService_Exporter_Base {
async init(destPath: string) {
private tempDir_: string;
private destPath_: string;
private rawExporter_: InteropService_Exporter_Raw;
public async init(destPath: string) {
if (await shim.fsDriver().isDirectory(destPath)) throw new Error(`Path is a directory: ${destPath}`);
this.tempDir_ = await this.temporaryDirectory_(false);
@@ -14,15 +20,15 @@ export default class InteropService_Exporter_Jex extends InteropService_Exporter
await this.rawExporter_.init(this.tempDir_);
}
async processItem(itemType: number, item: any) {
public async processItem(itemType: number, item: any) {
return this.rawExporter_.processItem(itemType, item);
}
async processResource(resource: any, filePath: string) {
public async processResource(resource: any, filePath: string) {
return this.rawExporter_.processResource(resource, filePath);
}
async close() {
public async close() {
const stats = await shim.fsDriver().readDirStats(this.tempDir_, { recursive: true });
const filePaths = stats.filter((a: any) => !a.isDirectory()).map((a: any) => a.path);

View File

@@ -227,6 +227,9 @@ rules.inlineLink = {
replacement: function (content, node, options) {
var href = filterLinkHref(node.getAttribute('href'))
console.info('RRRRRRRR', href);
if (!href) {
return getNamedAnchorFromLink(node, options) + filterLinkContent(content)
} else {