You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2025-12-23 23:33:01 +02:00
Compare commits
1 Commits
fix_link_c
...
linkify
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c0ceefab8a |
@@ -910,9 +910,6 @@ 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
3
.gitignore
vendored
@@ -899,9 +899,6 @@ 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
|
||||
|
||||
@@ -54,5 +54,4 @@ module.exports = {
|
||||
|
||||
testEnvironment: 'node',
|
||||
setupFilesAfterEnv: [`${__dirname}/jest.setup.js`],
|
||||
slowTestThreshold: 20,
|
||||
};
|
||||
|
||||
2
packages/app-cli/tests/.gitignore
vendored
2
packages/app-cli/tests/.gitignore
vendored
@@ -1,2 +1,2 @@
|
||||
test data/
|
||||
data/
|
||||
export/
|
||||
@@ -1,83 +1,97 @@
|
||||
"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
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
});
|
||||
@@ -1 +0,0 @@
|
||||
<a href="https://example.com"/>https://example.com</a>
|
||||
@@ -1 +0,0 @@
|
||||
https://example.com
|
||||
@@ -1,7 +1,6 @@
|
||||
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');
|
||||
@@ -218,8 +217,6 @@ describe('models_Note', function() {
|
||||
const t1 = r1.updated_time;
|
||||
const t2 = r2.updated_time;
|
||||
|
||||
const resourceDirE = markdownUtils.escapeLinkUrl(resourceDir);
|
||||
|
||||
const testCases = [
|
||||
[
|
||||
false,
|
||||
@@ -244,17 +241,17 @@ describe('models_Note', function() {
|
||||
[
|
||||
true,
|
||||
``,
|
||||
``,
|
||||
``,
|
||||
],
|
||||
[
|
||||
true,
|
||||
`  `,
|
||||
`  `,
|
||||
`  `,
|
||||
],
|
||||
[
|
||||
true,
|
||||
``,
|
||||
``,
|
||||
``,
|
||||
],
|
||||
];
|
||||
|
||||
|
||||
@@ -2,7 +2,8 @@ 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, exportDir } = require('./test-utils.js');
|
||||
|
||||
const { fileContentEqual, setupDatabaseAndSynchronizer, switchClient, checkThrowAsync } = 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');
|
||||
@@ -10,6 +11,10 @@ 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);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
|
||||
const fs = require('fs-extra');
|
||||
const { setupDatabaseAndSynchronizer, switchClient, exportDir } = require('./test-utils.js');
|
||||
const { setupDatabaseAndSynchronizer, switchClient } = 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,27 +10,29 @@ 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) => {
|
||||
@@ -72,7 +74,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) => {
|
||||
@@ -99,7 +101,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) => {
|
||||
@@ -116,7 +118,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);
|
||||
|
||||
@@ -126,7 +128,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) => {
|
||||
@@ -157,13 +159,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) => {
|
||||
@@ -187,14 +189,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) => {
|
||||
@@ -224,14 +226,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) => {
|
||||
@@ -267,8 +269,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.');
|
||||
@@ -276,7 +278,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) => {
|
||||
@@ -316,9 +318,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.');
|
||||
@@ -328,7 +330,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) => {
|
||||
@@ -349,7 +351,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');
|
||||
}));
|
||||
});
|
||||
|
||||
86
packages/app-cli/tests/test-utils-synchronizer.js
Normal file
86
packages/app-cli/tests/test-utils-synchronizer.js
Normal file
@@ -0,0 +1,86 @@
|
||||
"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
|
||||
@@ -100,11 +100,7 @@ FileApiDriverLocal.fsDriver_ = fsDriver;
|
||||
|
||||
const logDir = `${__dirname}/../tests/logs`;
|
||||
const baseTempDir = `${__dirname}/../tests/tmp/${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_}`;
|
||||
|
||||
const dataDir = `${__dirname}/data/${suiteName_}`;
|
||||
fs.mkdirpSync(logDir, 0o755);
|
||||
fs.mkdirpSync(baseTempDir, 0o755);
|
||||
fs.mkdirpSync(dataDir);
|
||||
@@ -306,11 +302,6 @@ 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}`;
|
||||
@@ -792,4 +783,4 @@ class TestApp extends BaseApplication {
|
||||
}
|
||||
}
|
||||
|
||||
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 };
|
||||
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 };
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const RNFS = require('react-native-fs');
|
||||
const FsDriverBase = require('@joplin/lib/fs-driver-base').default;
|
||||
const FsDriverBase = require('@joplin/lib/fs-driver-base');
|
||||
const RNFetchBlob = require('rn-fetch-blob').default;
|
||||
|
||||
class FsDriverRN extends FsDriverBase {
|
||||
|
||||
@@ -1,48 +1,15 @@
|
||||
import time from './time';
|
||||
import Setting from './models/Setting';
|
||||
import { filename, fileExtension } from './path-utils';
|
||||
const { filename, fileExtension } = require('./path-utils');
|
||||
const time = require('./time').default;
|
||||
const Setting = require('./models/Setting').default;
|
||||
const md5 = require('md5');
|
||||
|
||||
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) {
|
||||
class FsDriverBase {
|
||||
async isDirectory(path) {
|
||||
const stat = await this.stat(path);
|
||||
return !stat ? false : stat.isDirectory();
|
||||
}
|
||||
|
||||
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[]> {
|
||||
async readDirStatsHandleRecursion_(basePath, stat, output, options) {
|
||||
if (options.recursive && stat.isDirectory()) {
|
||||
const subPath = `${basePath}/${stat.path}`;
|
||||
const subStats = await this.readDirStats(subPath, options);
|
||||
@@ -56,7 +23,7 @@ export default class FsDriverBase {
|
||||
return output;
|
||||
}
|
||||
|
||||
public async findUniqueFilename(name: string, reservedNames: string[] = null): Promise<string> {
|
||||
async findUniqueFilename(name, reservedNames = null) {
|
||||
if (reservedNames === null) {
|
||||
reservedNames = [];
|
||||
}
|
||||
@@ -80,7 +47,7 @@ export default class FsDriverBase {
|
||||
}
|
||||
}
|
||||
|
||||
public async removeAllThatStartWith(dirPath: string, filenameStart: string) {
|
||||
async removeAllThatStartWith(dirPath, filenameStart) {
|
||||
if (!filenameStart || !dirPath) throw new Error('dirPath and filenameStart cannot be empty');
|
||||
|
||||
const stats = await this.readDirStats(dirPath);
|
||||
@@ -92,7 +59,7 @@ export default class FsDriverBase {
|
||||
}
|
||||
}
|
||||
|
||||
public async waitTillExists(path: string, timeout: number = 10000) {
|
||||
async waitTillExists(path, timeout = 10000) {
|
||||
const startTime = Date.now();
|
||||
|
||||
while (true) {
|
||||
@@ -105,7 +72,7 @@ export default class FsDriverBase {
|
||||
|
||||
// TODO: move out of here and make it part of joplin-renderer
|
||||
// or assign to option using .bind(fsDriver())
|
||||
public async cacheCssToFile(cssStrings: string[]) {
|
||||
async cacheCssToFile(cssStrings) {
|
||||
const cssString = Array.isArray(cssStrings) ? cssStrings.join('\n') : cssStrings;
|
||||
const cssFilePath = `${Setting.value('tempDir')}/${md5(escape(cssString))}.css`;
|
||||
if (!(await this.exists(cssFilePath))) {
|
||||
@@ -117,5 +84,6 @@ export default class FsDriverBase {
|
||||
mime: 'text/css',
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = FsDriverBase;
|
||||
@@ -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: Stat[] = [];
|
||||
let output = [];
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const item = items[i];
|
||||
const stat = await this.stat(`${path}/${item}`);
|
||||
|
||||
@@ -171,7 +171,7 @@ class Note extends BaseItem {
|
||||
useAbsolutePaths: false,
|
||||
}, options);
|
||||
|
||||
let pathsToTry = [];
|
||||
const pathsToTry = [];
|
||||
if (options.useAbsolutePaths) {
|
||||
pathsToTry.push(`file://${Setting.value('resourceDir')}`);
|
||||
pathsToTry.push(`file://${shim.pathRelativeToCwd(Setting.value('resourceDir'))}`);
|
||||
@@ -179,21 +179,6 @@ 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) {
|
||||
|
||||
@@ -590,6 +590,8 @@ class Setting extends BaseModel {
|
||||
|
||||
'markdown.plugin.softbreaks': { value: false, type: SettingItemType.Bool, section: 'markdownPlugins', public: true, appTypes: ['mobile', 'desktop'], label: () => `${_('Enable soft breaks')}${wysiwygYes}` },
|
||||
'markdown.plugin.typographer': { value: false, type: SettingItemType.Bool, section: 'markdownPlugins', public: true, appTypes: ['mobile', 'desktop'], label: () => `${_('Enable typographer support')}${wysiwygYes}` },
|
||||
'markdown.plugin.linkify': { value: true, type: SettingItemType.Bool, section: 'markdownPlugins', public: true, appTypes: ['mobile', 'desktop'], label: () => `${_('Enable Linkify')}${wysiwygYes}` },
|
||||
|
||||
'markdown.plugin.katex': { value: true, type: SettingItemType.Bool, section: 'markdownPlugins', public: true, appTypes: ['mobile', 'desktop'], label: () => `${_('Enable math expressions')}${wysiwygYes}` },
|
||||
'markdown.plugin.fountain': { value: false, type: SettingItemType.Bool, section: 'markdownPlugins', public: true, appTypes: ['mobile', 'desktop'], label: () => `${_('Enable Fountain syntax support')}${wysiwygYes}` },
|
||||
'markdown.plugin.mermaid': { value: true, type: SettingItemType.Bool, section: 'markdownPlugins', public: true, appTypes: ['mobile', 'desktop'], label: () => `${_('Enable Mermaid diagrams support')}${wysiwygYes}` },
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
import { _ } from '../../locale';
|
||||
import InteropService_Exporter_Base from './InteropService_Exporter_Base';
|
||||
import InteropService_Exporter_Raw from './InteropService_Exporter_Raw';
|
||||
import shim from '../../shim';
|
||||
|
||||
const InteropService_Exporter_Base = require('./InteropService_Exporter_Base').default;
|
||||
const InteropService_Exporter_Raw = require('./InteropService_Exporter_Raw').default;
|
||||
const fs = require('fs-extra');
|
||||
const shim = require('../../shim').default;
|
||||
|
||||
export default class InteropService_Exporter_Jex extends InteropService_Exporter_Base {
|
||||
|
||||
private tempDir_: string;
|
||||
private destPath_: string;
|
||||
private rawExporter_: InteropService_Exporter_Raw;
|
||||
|
||||
public async init(destPath: string) {
|
||||
async init(destPath: string) {
|
||||
if (await shim.fsDriver().isDirectory(destPath)) throw new Error(`Path is a directory: ${destPath}`);
|
||||
|
||||
this.tempDir_ = await this.temporaryDirectory_(false);
|
||||
@@ -20,15 +14,15 @@ export default class InteropService_Exporter_Jex extends InteropService_Exporter
|
||||
await this.rawExporter_.init(this.tempDir_);
|
||||
}
|
||||
|
||||
public async processItem(itemType: number, item: any) {
|
||||
async processItem(itemType: number, item: any) {
|
||||
return this.rawExporter_.processItem(itemType, item);
|
||||
}
|
||||
|
||||
public async processResource(resource: any, filePath: string) {
|
||||
async processResource(resource: any, filePath: string) {
|
||||
return this.rawExporter_.processResource(resource, filePath);
|
||||
}
|
||||
|
||||
public async close() {
|
||||
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);
|
||||
|
||||
|
||||
@@ -378,7 +378,7 @@ export default class MdToHtml {
|
||||
const markdownIt = new MarkdownIt({
|
||||
breaks: !this.pluginEnabled('softbreaks'),
|
||||
typographer: this.pluginEnabled('typographer'),
|
||||
linkify: true,
|
||||
linkify: this.pluginEnabled('linkify'),
|
||||
html: true,
|
||||
highlight: (str: string, lang: string) => {
|
||||
let outputCodeHtml = '';
|
||||
@@ -471,7 +471,7 @@ export default class MdToHtml {
|
||||
}
|
||||
}
|
||||
|
||||
setupLinkify(markdownIt);
|
||||
if (this.pluginEnabled('linkify')) setupLinkify(markdownIt);
|
||||
|
||||
const renderedBody = markdownIt.render(body, context);
|
||||
|
||||
|
||||
@@ -26,4 +26,10 @@ module.exports = function(markdownIt) {
|
||||
|
||||
return BAD_PROTO_RE.test(str) ? (GOOD_DATA_RE.test(str) ? true : false) : true;
|
||||
};
|
||||
|
||||
markdownIt.linkify.set({
|
||||
'fuzzyLink': false,
|
||||
'fuzzyIP': false,
|
||||
'fuzzyEmail': false,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -227,9 +227,6 @@ 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 {
|
||||
|
||||
@@ -146,6 +146,7 @@ Note that the functionality added by these plugins is not part of the CommonMark
|
||||
|--------|--------|-------------|---------|------------|
|
||||
| Soft breaks | See [breaks](https://markdown-it.github.io/#md3=%7B%22source%22%3A%22This%20is%20line1%5CnThis%20is%20line2%5Cn%5CnThis%20is%20a%20line%20with%202%20trailing%20spaces%20%20%5CnNext%20line%5Cn%5CnClick%20the%20%60breaks%60%20checkbox%20above%20to%20see%20the%20difference.%5CnJoplin%27s%20default%20is%20hard%20breaks%20%28checked%20%60breaks%60%20checkbox%29.%22%2C%22defaults%22%3A%7B%22html%22%3Afalse%2C%22xhtmlOut%22%3Afalse%2C%22breaks%22%3Afalse%2C%22langPrefix%22%3A%22language-%22%2C%22linkify%22%3Afalse%2C%22typographer%22%3Afalse%2C%22_highlight%22%3Afalse%2C%22_strict%22%3Afalse%2C%22_view%22%3A%22html%22%7D%7D) markdown-it demo| Joplin uses hard breaks by default, which means that a line break is rendered as `<br>`. Enable soft breaks for traditional markdown line-break behaviour. | no | [View](https://joplinapp.org/images/md_plugins/softbreaks_plugin.jpg)
|
||||
| Typographer | See [typographer](https://markdown-it.github.io/#md3=%7B%22source%22%3A%22%23%20Typographic%20replacements%5Cn%5Cn%28c%29%20%28C%29%20%28r%29%20%28R%29%20%28tm%29%20%28TM%29%20%28p%29%20%28P%29%20%2B-%5Cn%5Cntest..%20test...%20test.....%20test%3F.....%20test!....%5Cn%5Cn!!!!!!%20%3F%3F%3F%3F%20%2C%2C%20%20--%20---%5Cn%5Cn%5C%22Smartypants%2C%20double%20quotes%5C%22%20and%20%27single%20quotes%27%5Cn%22%2C%22defaults%22%3A%7B%22html%22%3Afalse%2C%22xhtmlOut%22%3Afalse%2C%22breaks%22%3Afalse%2C%22langPrefix%22%3A%22language-%22%2C%22linkify%22%3Atrue%2C%22typographer%22%3Atrue%2C%22_highlight%22%3Atrue%2C%22_strict%22%3Afalse%2C%22_view%22%3A%22html%22%7D%7D) markdown-it demo | Does typographic replacements, (c) -> © and so on | no | [View](https://joplinapp.org/images/md_plugins/typographer_plugin.jpg) |
|
||||
| Linkify | See [linkify](https://markdown-it.github.io/#md3=%7B%22source%22%3A%22Use%20the%20Linkify%20checkbox%20to%20switch%20link-detection%20on%20and%20off.%5Cn%5Cn%2A%2AThese%20links%20are%20auto-detected%3A%2A%2A%5Cn%5Cnhttps%3A%2F%2Fexample.com%5Cn%5Cnexample.com%5Cn%5Cntest%40example.com%5Cn%5Cn%2A%2AThese%20are%20always%20links%3A%2A%2A%5Cn%5Cn%5Blink%5D%28https%3A%2F%2Fjoplinapp.org%29%5Cn%5Cn%3Chttps%3A%2F%2Fexample.com%3E%22%2C%22defaults%22%3A%7B%22html%22%3Afalse%2C%22xhtmlOut%22%3Afalse%2C%22breaks%22%3Afalse%2C%22langPrefix%22%3A%22language-%22%2C%22linkify%22%3Atrue%2C%22typographer%22%3Atrue%2C%22_highlight%22%3Atrue%2C%22_strict%22%3Afalse%2C%22_view%22%3A%22html%22%7D%7D) markdown-it demo | Auto-detects URLs and convert them to clickable links | yes | |
|
||||
| [Katex](https://katex.org) | `$$math expr$$` or `$math$` | [See above](#math-notation) | yes | [View](https://joplinapp.org/images/md_plugins/katex_plugin.jpg) |
|
||||
| [Mark](https://github.com/markdown-it/markdown-it-mark) | `==marked==` | Transforms into `<mark>marked</mark>` (highlighted) | yes | [View](https://joplinapp.org/images/md_plugins/mark_plugin.jpg) |
|
||||
| [Footnote](https://github.com/markdown-it/markdown-it-footnote) | `Simples inline footnote ^[I'm inline!]` | See [plugin page](https://github.com/markdown-it/markdown-it-footnote) for full description | yes | [View](https://joplinapp.org/images/md_plugins/footnote_plugin.jpg) |
|
||||
|
||||
Reference in New Issue
Block a user