mirror of
https://github.com/streetwriters/notesnook.git
synced 2026-08-30 18:48:27 +02:00
Compare commits
27 Commits
3.3.2-andr
...
docs/self-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
caf217907c | ||
|
|
2341293d92 | ||
|
|
215387f8a9 | ||
|
|
534b8c5ad0 | ||
|
|
b7b2ead9eb | ||
|
|
b3e34d68e1 | ||
|
|
39079612aa | ||
|
|
be1af3111f | ||
|
|
8dcff49797 | ||
|
|
687fb4e105 | ||
|
|
25ae780ad8 | ||
|
|
b695367e6c | ||
|
|
bb274a84fe | ||
|
|
364dccbf7a | ||
|
|
110534edec | ||
|
|
00e2190b2d | ||
|
|
e5c3fc667e | ||
|
|
9aef8194f9 | ||
|
|
f76e0b7931 | ||
|
|
0457e4417f | ||
|
|
dae8b5c542 | ||
|
|
60107dd77f | ||
|
|
4f3e19aaa2 | ||
|
|
c77050eccc | ||
|
|
f40ccdae41 | ||
|
|
0a0aa09eea | ||
|
|
e2c55fb758 |
@@ -7,7 +7,7 @@ runs:
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20.x
|
||||
node-version: 22.x
|
||||
cache: "npm"
|
||||
cache-dependency-path: |
|
||||
package-lock.json
|
||||
|
||||
3
.github/workflows/core.tests.yml
vendored
3
.github/workflows/core.tests.yml
vendored
@@ -52,7 +52,6 @@ jobs:
|
||||
echo "USER_PASSWORD=${{ secrets.USER_PASSWORD }}" >> $GITHUB_ENV
|
||||
echo "USER_TOTP_SECRET=${{ secrets.USER_TOTP_SECRET }}" >> $GITHUB_ENV
|
||||
echo "USER_HASHED_PASSWORD=${{ secrets.USER_HASHED_PASSWORD }}" >> $GITHUB_ENV
|
||||
echo "NX_CLOUD_ACCESS_TOKEN=${{ secrets.NX_CLOUD_ACCESS_TOKEN }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Run all @notesnook/core tests
|
||||
run: npx nx test:e2e @notesnook/core
|
||||
run: npm run tx @notesnook/core:test:e2e
|
||||
|
||||
20
.github/workflows/desktop.publish.yml
vendored
20
.github/workflows/desktop.publish.yml
vendored
@@ -65,17 +65,13 @@ jobs:
|
||||
npm i --cpu x64 sqlite-better-trigram
|
||||
working-directory: ./apps/desktop
|
||||
|
||||
- name: Setup environment
|
||||
run: |
|
||||
echo "NX_CLOUD_ACCESS_TOKEN=${{ secrets.NX_CLOUD_ACCESS_TOKEN }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Generate desktop build (stable)
|
||||
if: ${{ inputs.release-track == 'stable' }}
|
||||
run: npx nx build:desktop @notesnook/web
|
||||
run: npm run tx @notesnook/web:build:desktop
|
||||
|
||||
- name: Generate desktop build (beta)
|
||||
if: ${{ inputs.release-track == 'beta' }}
|
||||
run: BETA=true npx nx build:desktop @notesnook/web
|
||||
run: BETA=true npm run tx @notesnook/web:build:desktop
|
||||
|
||||
- name: Build desktop bundle
|
||||
working-directory: ./apps/desktop
|
||||
@@ -199,9 +195,9 @@ jobs:
|
||||
CSC_LINK: ${{ secrets.mac_certs }}
|
||||
CSC_KEY_PASSWORD: ${{ secrets.mac_certs_password }}
|
||||
run: |
|
||||
npx nx run release --project @notesnook/desktop -- --variant=mas
|
||||
npm run tx @notesnook/desktop:release -- --variant=mas
|
||||
cd apps/desktop
|
||||
yarn electron-builder --config=electron-builder.config.js --mac mas --universal -p never
|
||||
working-directory: ./apps/desktop
|
||||
|
||||
- name: Build zip and dmg
|
||||
env:
|
||||
@@ -212,13 +208,13 @@ jobs:
|
||||
APPLE_API_KEY_ID: ${{ secrets.api_key_id }}
|
||||
APPLE_API_ISSUER: ${{ secrets.api_key_issuer_id }}
|
||||
run: |
|
||||
npx nx run release --project @notesnook/desktop
|
||||
npm run tx @notesnook/desktop:release
|
||||
cd apps/desktop
|
||||
if [ ${{ inputs.publish-github }} == true ]; then
|
||||
yarn electron-builder --config=electron-builder.config.js --mac zip dmg --arm64 --x64 -p always
|
||||
else
|
||||
yarn electron-builder --config=electron-builder.config.js --mac zip dmg --arm64 --x64 -p never
|
||||
fi
|
||||
working-directory: ./apps/desktop
|
||||
|
||||
- name: Deploy to Testflight
|
||||
if: inputs.publish-apple && steps.appstore.outputs.app-version-latest != steps.app_metadata.outputs.app_version
|
||||
@@ -271,7 +267,7 @@ jobs:
|
||||
working-directory: ./apps/desktop
|
||||
|
||||
- name: Build Electron wrapper
|
||||
run: npx nx run release --project @notesnook/desktop
|
||||
run: npm run tx @notesnook/desktop:release
|
||||
working-directory: ./apps/desktop
|
||||
|
||||
- name: Build snap
|
||||
@@ -339,7 +335,7 @@ jobs:
|
||||
working-directory: ./apps/desktop
|
||||
|
||||
- name: Build Electron wrapper
|
||||
run: npx nx run release --project @notesnook/desktop
|
||||
run: npm run tx @notesnook/desktop:release
|
||||
working-directory: ./apps/desktop
|
||||
|
||||
# - name: Build snap
|
||||
|
||||
12
.github/workflows/desktop.tests.yml
vendored
12
.github/workflows/desktop.tests.yml
vendored
@@ -33,12 +33,8 @@ jobs:
|
||||
npm ci --ignore-scripts --prefer-offline --no-audit
|
||||
npm run bootstrap -- --scope=web
|
||||
|
||||
- name: Setup environment
|
||||
run: |
|
||||
echo "NX_CLOUD_ACCESS_TOKEN=${{ secrets.NX_CLOUD_ACCESS_TOKEN }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Generate desktop build
|
||||
run: npx nx build:desktop @notesnook/web
|
||||
run: npm run tx @notesnook/web:build:desktop
|
||||
|
||||
- name: Archive build artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
@@ -70,7 +66,7 @@ jobs:
|
||||
npm run bootstrap -- --scope=desktop
|
||||
|
||||
- name: Build Electron wrapper
|
||||
run: npx nx run release --project @notesnook/desktop
|
||||
run: npm run tx @notesnook/desktop:release
|
||||
working-directory: ./apps/desktop
|
||||
|
||||
- name: Build app
|
||||
@@ -114,7 +110,7 @@ jobs:
|
||||
npm run bootstrap -- --scope=desktop
|
||||
|
||||
- name: Build Electron wrapper
|
||||
run: npx nx run release --project @notesnook/desktop
|
||||
run: npm run tx @notesnook/desktop:release
|
||||
working-directory: ./apps/desktop
|
||||
|
||||
- name: Build app
|
||||
@@ -164,7 +160,7 @@ jobs:
|
||||
working-directory: ./apps/desktop
|
||||
|
||||
- name: Build Electron wrapper
|
||||
run: npx nx run release --project @notesnook/desktop
|
||||
run: npm run tx @notesnook/desktop:release
|
||||
working-directory: ./apps/desktop
|
||||
|
||||
- name: Build app
|
||||
|
||||
8
.github/workflows/editor.tests.yml
vendored
8
.github/workflows/editor.tests.yml
vendored
@@ -38,12 +38,8 @@ jobs:
|
||||
npm ci --ignore-scripts --prefer-offline --no-audit
|
||||
npm run bootstrap -- --scope=editor
|
||||
|
||||
- name: Setup environment
|
||||
run: |
|
||||
echo "NX_CLOUD_ACCESS_TOKEN=${{ secrets.NX_CLOUD_ACCESS_TOKEN }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Build editor
|
||||
run: npx nx build @notesnook/editor
|
||||
run: npm run tx @notesnook/editor:build
|
||||
|
||||
- name: Run all @notesnook/editor tests
|
||||
run: npx nx test @notesnook/editor
|
||||
run: npm run tx @notesnook/editor:test
|
||||
|
||||
2
.github/workflows/ios.publish.yml
vendored
2
.github/workflows/ios.publish.yml
vendored
@@ -25,7 +25,7 @@ jobs:
|
||||
npm run bootstrap -- --scope=mobile
|
||||
|
||||
- name: Build packages
|
||||
run: npx nx run @notesnook/mobile:build
|
||||
run: npm run tx @notesnook/mobile:build
|
||||
|
||||
- name: ccache
|
||||
uses: hendrikmuhs/ccache-action@v1.2.11
|
||||
|
||||
2
.github/workflows/monograph.publish.yml
vendored
2
.github/workflows/monograph.publish.yml
vendored
@@ -39,7 +39,7 @@ jobs:
|
||||
echo ::set-output name=app_version::$(cat package.json | jq -r .version)
|
||||
|
||||
- name: Generate build
|
||||
run: npx nx build @notesnook/monograph
|
||||
run: npm run tx @notesnook/monograph:build
|
||||
|
||||
# Setup Buildx
|
||||
- name: Docker Setup Buildx
|
||||
|
||||
1
.github/workflows/theme-builder.publish.yml
vendored
1
.github/workflows/theme-builder.publish.yml
vendored
@@ -20,7 +20,6 @@ jobs:
|
||||
|
||||
- name: Setup environment
|
||||
run: |
|
||||
echo "NX_CLOUD_ACCESS_TOKEN=${{ secrets.NX_CLOUD_ACCESS_TOKEN }}" >> $GITHUB_ENV
|
||||
echo "CLOUDFLARE_ACCOUNT_ID=${{ secrets.CLOUDFLARE_ACCOUNT_ID }}" >> $GITHUB_ENV
|
||||
echo "CLOUDFLARE_API_TOKEN=${{ secrets.CLOUDFLARE_API_TOKEN }}" >> $GITHUB_ENV
|
||||
|
||||
|
||||
1
.github/workflows/vericrypt.publish.yml
vendored
1
.github/workflows/vericrypt.publish.yml
vendored
@@ -20,7 +20,6 @@ jobs:
|
||||
|
||||
- name: Setup environment
|
||||
run: |
|
||||
echo "NX_CLOUD_ACCESS_TOKEN=${{ secrets.NX_CLOUD_ACCESS_TOKEN }}" >> $GITHUB_ENV
|
||||
echo "CLOUDFLARE_ACCOUNT_ID=${{ secrets.CLOUDFLARE_ACCOUNT_ID }}" >> $GITHUB_ENV
|
||||
echo "CLOUDFLARE_API_TOKEN=${{ secrets.CLOUDFLARE_API_TOKEN }}" >> $GITHUB_ENV
|
||||
|
||||
|
||||
4
.github/workflows/web.benchmarks.yml
vendored
4
.github/workflows/web.benchmarks.yml
vendored
@@ -27,10 +27,6 @@ jobs:
|
||||
npm ci --ignore-scripts --prefer-offline --no-audit
|
||||
npm run bootstrap -- --scope=web
|
||||
|
||||
- name: Setup environment
|
||||
run: |
|
||||
echo "NX_CLOUD_ACCESS_TOKEN=${{ secrets.NX_CLOUD_ACCESS_TOKEN }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Build
|
||||
run: npm run build:web
|
||||
|
||||
|
||||
1
.github/workflows/web.publish.yml
vendored
1
.github/workflows/web.publish.yml
vendored
@@ -30,7 +30,6 @@ jobs:
|
||||
|
||||
- name: Setup environment
|
||||
run: |
|
||||
echo "NX_CLOUD_ACCESS_TOKEN=${{ secrets.NX_CLOUD_ACCESS_TOKEN }}" >> $GITHUB_ENV
|
||||
echo "CLOUDFLARE_ACCOUNT_ID=${{ secrets.CLOUDFLARE_ACCOUNT_ID }}" >> $GITHUB_ENV
|
||||
echo "CLOUDFLARE_API_TOKEN=${{ secrets.CLOUDFLARE_API_TOKEN }}" >> $GITHUB_ENV
|
||||
|
||||
|
||||
4
.github/workflows/web.tests.yml
vendored
4
.github/workflows/web.tests.yml
vendored
@@ -49,10 +49,6 @@ jobs:
|
||||
npm ci --ignore-scripts --prefer-offline --no-audit
|
||||
npm run bootstrap -- --scope=web
|
||||
|
||||
- name: Setup environment
|
||||
run: |
|
||||
echo "NX_CLOUD_ACCESS_TOKEN=${{ secrets.NX_CLOUD_ACCESS_TOKEN }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Generate test build
|
||||
run: npm run build:test:web
|
||||
|
||||
|
||||
@@ -27,7 +27,6 @@ Notesnook is built using the following technologies:
|
||||
3. React Native — For mobile apps we are using React Native
|
||||
4. Electron — For desktop app
|
||||
5. NPM — listed here because we **don't** use Yarn or PNPM or XYZ across any of our projects.
|
||||
6. Nx — maintaining monorepos is hard but Nx makes it easier.
|
||||
|
||||
> **Note: Each project in the monorepo contains its own architecture details which you can refer to.**
|
||||
|
||||
|
||||
573
apps/desktop/package-lock.json
generated
573
apps/desktop/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -19,14 +19,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import { useRoute } from "@react-navigation/native";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import {
|
||||
ScrollView,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
useWindowDimensions
|
||||
} from "react-native";
|
||||
import { TouchableOpacity, View, useWindowDimensions } from "react-native";
|
||||
import { SheetManager } from "react-native-actions-sheet";
|
||||
import { KeyboardAwareScrollView } from "react-native-keyboard-aware-scroll-view";
|
||||
import { DDS } from "../../services/device-detection";
|
||||
import { eSendEvent } from "../../services/event-manager";
|
||||
import Navigation from "../../services/navigation";
|
||||
@@ -47,8 +44,7 @@ import { hideAuth } from "./common";
|
||||
import { ForgotPassword } from "./forgot-password";
|
||||
import { AuthHeader } from "./header";
|
||||
import { useLogin } from "./use-login";
|
||||
import { useRoute } from "@react-navigation/native";
|
||||
import { KeyboardAwareScrollView } from "react-native-keyboard-aware-scroll-view";
|
||||
import SettingsService from "../../services/settings";
|
||||
|
||||
const LoginSteps = {
|
||||
emailAuth: 1,
|
||||
@@ -81,7 +77,7 @@ export const Login = ({ changeMode }) => {
|
||||
}
|
||||
}, 5000);
|
||||
|
||||
if (!PremiumService.get()) {
|
||||
if (!PremiumService.get() && !SettingsService.getProperty("serverUrls")) {
|
||||
Navigation.navigate("PayWall", {
|
||||
context: "signup",
|
||||
state: route.params.state,
|
||||
|
||||
@@ -19,6 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import { useRoute } from "@react-navigation/native";
|
||||
import React, { useRef, useState } from "react";
|
||||
import { TouchableOpacity, View, useWindowDimensions } from "react-native";
|
||||
import { KeyboardAwareScrollView } from "react-native-keyboard-aware-scroll-view";
|
||||
@@ -38,7 +39,6 @@ import Heading from "../ui/typography/heading";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
import { AuthHeader } from "./header";
|
||||
import { SignupContext } from "./signup-context";
|
||||
import { useRoute } from "@react-navigation/native";
|
||||
|
||||
const SignupSteps = {
|
||||
signup: 0,
|
||||
@@ -91,11 +91,13 @@ export const Signup = ({ changeMode, welcome }) => {
|
||||
setLastSynced(await db.lastSynced());
|
||||
clearMessage();
|
||||
setEmailVerifyMessage();
|
||||
Navigation.navigate("PayWall", {
|
||||
canGoBack: false,
|
||||
state: route.params.state,
|
||||
context: "signup"
|
||||
});
|
||||
if (!SettingsService.getProperty("serverUrls")) {
|
||||
Navigation.navigate("PayWall", {
|
||||
canGoBack: false,
|
||||
state: route.params.state,
|
||||
context: "signup"
|
||||
});
|
||||
}
|
||||
return true;
|
||||
} catch (e) {
|
||||
setCurrentStep(SignupSteps.signup);
|
||||
|
||||
@@ -60,6 +60,7 @@ import usePricingPlans, {
|
||||
PricingPlan
|
||||
} from "../../hooks/use-pricing-plans";
|
||||
import Navigation, { NavigationProps } from "../../services/navigation";
|
||||
import PremiumService from "../../services/premium";
|
||||
import { getElevationStyle } from "../../utils/elevation";
|
||||
import { openLinkInBrowser } from "../../utils/functions";
|
||||
import { AppFontSize, defaultBorderRadius } from "../../utils/size";
|
||||
@@ -74,7 +75,6 @@ import { IconButton } from "../ui/icon-button";
|
||||
import { SvgView } from "../ui/svg";
|
||||
import Heading from "../ui/typography/heading";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
import PremiumService from "../../services/premium";
|
||||
|
||||
const Steps = {
|
||||
select: 1,
|
||||
@@ -114,15 +114,8 @@ const PayWall = (props: NavigationProps<"PayWall">) => {
|
||||
routeParams.state?.planId,
|
||||
routeParams.state?.productId
|
||||
);
|
||||
console.log(
|
||||
"selectPlan",
|
||||
routeParams.state?.planId,
|
||||
routeParams.state?.productId,
|
||||
(pricingPlans.selectedProduct as any).productId
|
||||
);
|
||||
}
|
||||
setStep(Steps.buy);
|
||||
console.log("Buy step");
|
||||
}
|
||||
}, [routeParams.state]);
|
||||
|
||||
@@ -949,7 +942,6 @@ const PricingPlanCard = ({
|
||||
regionalDiscount?.sku ||
|
||||
`notesnook.${plan.id}.${annualBilling ? "yearly" : "monthly"}`
|
||||
];
|
||||
console.log(regionalDiscount?.sku);
|
||||
const price = pricingPlans?.getPrice(
|
||||
product as RNIap.Subscription,
|
||||
pricingPlans.hasTrialOffer(plan.id, product?.productId) ? 1 : 0,
|
||||
@@ -962,6 +954,7 @@ const PricingPlanCard = ({
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (pricingPlans?.isGithubRelease) return;
|
||||
pricingPlans
|
||||
?.getRegionalDiscount(
|
||||
plan.id,
|
||||
@@ -980,7 +973,7 @@ const PricingPlanCard = ({
|
||||
pricingPlans.isSubscribed();
|
||||
|
||||
const isNotReady =
|
||||
pricingPlans?.loadingPlans || (!price && !WebPlan?.price.gross);
|
||||
pricingPlans?.loadingPlans || (!price && !WebPlan?.price?.gross);
|
||||
|
||||
return (
|
||||
<TouchableOpacity
|
||||
@@ -1112,7 +1105,7 @@ const PricingPlanCard = ({
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{pricingPlans?.loadingPlans || (!price && !WebPlan?.price.gross) ? (
|
||||
{pricingPlans?.loadingPlans || (!price && !WebPlan?.price?.gross) ? (
|
||||
<ActivityIndicator size="small" color={colors.primary.accent} />
|
||||
) : (
|
||||
<View>
|
||||
@@ -1120,7 +1113,7 @@ const PricingPlanCard = ({
|
||||
{isFreePlan
|
||||
? "0.00"
|
||||
: price ||
|
||||
`${WebPlan?.price.currency} ${WebPlan?.price.gross}`}{" "}
|
||||
`${WebPlan?.price?.currency} ${WebPlan?.price?.gross}`}{" "}
|
||||
<Paragraph>/{strings.month()}</Paragraph>
|
||||
</Paragraph>
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
} from "../../../services/event-manager";
|
||||
import Navigation from "../../../services/navigation";
|
||||
import PremiumService from "../../../services/premium";
|
||||
import SettingsService from "../../../services/settings";
|
||||
import { useUserStore } from "../../../stores/use-user-store";
|
||||
import { eCloseSheet } from "../../../utils/events";
|
||||
import { AppFontSize } from "../../../utils/size";
|
||||
@@ -37,7 +38,6 @@ export default function PaywallSheet<Tid extends FeatureId>(props: {
|
||||
const { colors } = useThemeColors();
|
||||
const pricingPlans = usePricingPlans();
|
||||
useEffect(() => {
|
||||
console.log("PaywallSheet mounted with feature:", props.feature);
|
||||
ToastManager.hide();
|
||||
if (!props.feature.availableOn) return;
|
||||
const plan = pricingPlans.pricingPlans.find(
|
||||
@@ -55,16 +55,17 @@ export default function PaywallSheet<Tid extends FeatureId>(props: {
|
||||
}, []);
|
||||
|
||||
const isSubscribedOnWeb =
|
||||
(PremiumService.get() &&
|
||||
pricingPlans.user?.subscription.provider ===
|
||||
SubscriptionProvider.PADDLE) ||
|
||||
pricingPlans.user?.subscription.provider ===
|
||||
SubscriptionProvider.STREETWRITERS;
|
||||
PremiumService.get() &&
|
||||
(pricingPlans.user?.subscription?.provider ===
|
||||
SubscriptionProvider.PADDLE ||
|
||||
pricingPlans.user?.subscription?.provider ===
|
||||
SubscriptionProvider.STREETWRITERS);
|
||||
|
||||
const isCurrentPlatform =
|
||||
(pricingPlans.user?.subscription.provider === SubscriptionProvider.APPLE &&
|
||||
(pricingPlans.user?.subscription?.provider === SubscriptionProvider.APPLE &&
|
||||
Platform.OS === "ios") ||
|
||||
(pricingPlans.user?.subscription.provider === SubscriptionProvider.GOOGLE &&
|
||||
(pricingPlans.user?.subscription?.provider ===
|
||||
SubscriptionProvider.GOOGLE &&
|
||||
Platform.OS === "android");
|
||||
|
||||
return !pricingPlans.currentPlan ? null : (
|
||||
@@ -193,25 +194,28 @@ export default function PaywallSheet<Tid extends FeatureId>(props: {
|
||||
width: "100%"
|
||||
}}
|
||||
onPress={() => {
|
||||
if (
|
||||
pricingPlans.user?.subscription.plan ===
|
||||
SubscriptionPlan.LEGACY_PRO ||
|
||||
!isCurrentPlatform
|
||||
) {
|
||||
ToastManager.show({
|
||||
message: strings.cannotChangePlan(),
|
||||
context: "local"
|
||||
});
|
||||
return;
|
||||
if (PremiumService.get()) {
|
||||
if (
|
||||
pricingPlans.user?.subscription.plan ===
|
||||
SubscriptionPlan.LEGACY_PRO ||
|
||||
!isCurrentPlatform
|
||||
) {
|
||||
ToastManager.show({
|
||||
message: strings.cannotChangePlan(),
|
||||
context: "local"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (isSubscribedOnWeb) {
|
||||
ToastManager.show({
|
||||
message: strings.changePlanOnWeb(),
|
||||
context: "local"
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (isSubscribedOnWeb) {
|
||||
ToastManager.show({
|
||||
message: strings.changePlanOnWeb(),
|
||||
context: "local"
|
||||
});
|
||||
return;
|
||||
}
|
||||
eSendEvent(eCloseSheet);
|
||||
if (!useUserStore.getState().user) {
|
||||
Navigation.navigate("Auth", {
|
||||
@@ -255,6 +259,7 @@ export default function PaywallSheet<Tid extends FeatureId>(props: {
|
||||
}
|
||||
|
||||
PaywallSheet.present = <Tid extends FeatureId>(feature: FeatureResult<Tid>) => {
|
||||
if (SettingsService.getProperty("serverUrls")) return;
|
||||
presentSheet({
|
||||
component: <PaywallSheet feature={feature} />
|
||||
});
|
||||
|
||||
@@ -20,6 +20,7 @@ import Paragraph from "../../ui/typography/paragraph";
|
||||
import { SubscriptionPlan, SubscriptionProvider } from "@notesnook/core";
|
||||
import { useUserStore } from "../../../stores/use-user-store";
|
||||
import PremiumService from "../../../services/premium";
|
||||
import SettingsService from "../../../services/settings";
|
||||
|
||||
export function PlanLimits() {
|
||||
const { colors } = useThemeColors();
|
||||
@@ -35,9 +36,9 @@ export function PlanLimits() {
|
||||
}, []);
|
||||
|
||||
const isCurrentPlatform =
|
||||
(user?.subscription.provider === SubscriptionProvider.APPLE &&
|
||||
(user?.subscription?.provider === SubscriptionProvider.APPLE &&
|
||||
Platform.OS === "ios") ||
|
||||
(user?.subscription.provider === SubscriptionProvider.GOOGLE &&
|
||||
(user?.subscription?.provider === SubscriptionProvider.GOOGLE &&
|
||||
Platform.OS === "android");
|
||||
|
||||
return (
|
||||
@@ -84,10 +85,11 @@ export function PlanLimits() {
|
||||
</View>
|
||||
))}
|
||||
|
||||
{(user?.subscription.provider === SubscriptionProvider.PADDLE ||
|
||||
user?.subscription.provider === SubscriptionProvider.STREETWRITERS ||
|
||||
{((user?.subscription?.provider === SubscriptionProvider.PADDLE ||
|
||||
user?.subscription?.provider === SubscriptionProvider.STREETWRITERS ||
|
||||
!isCurrentPlatform) &&
|
||||
PremiumService.get() ? null : (
|
||||
PremiumService.get()) ||
|
||||
SettingsService.getProperty("serverUrls") ? null : (
|
||||
<Button
|
||||
title={strings.changePlan()}
|
||||
onPress={() => {
|
||||
|
||||
@@ -24,10 +24,11 @@ import { FlatList, View } from "react-native";
|
||||
import { DraxProvider, DraxScrollView } from "react-native-drax";
|
||||
import { db } from "../../common/database";
|
||||
import Navigation from "../../services/navigation";
|
||||
import SettingsService from "../../services/settings";
|
||||
import { useMenuStore } from "../../stores/use-menu-store";
|
||||
import { useSettingStore } from "../../stores/use-setting-store";
|
||||
import { useUserStore } from "../../stores/use-user-store";
|
||||
import { SUBSCRIPTION_STATUS } from "../../utils/constants";
|
||||
import { MenuItemsList } from "../../utils/menu-items";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
import ReorderableList from "../list/reorderable-list";
|
||||
import { MenuItemProperties } from "../sheets/menu-item-properties";
|
||||
@@ -36,7 +37,6 @@ import { ColorSection } from "./color-section";
|
||||
import { MenuItem } from "./menu-item";
|
||||
import { PinnedSection } from "./pinned-section";
|
||||
import { SideMenuHeader } from "./side-menu-header";
|
||||
import { MenuItemsList } from "../../utils/menu-items";
|
||||
|
||||
const pro = {
|
||||
title: strings.upgradePlan(),
|
||||
@@ -140,9 +140,10 @@ export function SideMenuHome() {
|
||||
paddingVertical: DefaultAppStyles.GAP_VERTICAL
|
||||
}}
|
||||
>
|
||||
{subscriptionType === SubscriptionPlan.FREE ||
|
||||
!subscriptionType ||
|
||||
!user ? (
|
||||
{(subscriptionType === SubscriptionPlan.FREE ||
|
||||
!subscriptionType ||
|
||||
!user) &&
|
||||
!SettingsService.getProperty("serverUrls") ? (
|
||||
<Button
|
||||
title={pro.title}
|
||||
style={{
|
||||
|
||||
@@ -85,6 +85,7 @@ export default function useFeatureManager() {
|
||||
negativeText: strings.cancel(),
|
||||
positivePress: async () => {
|
||||
eSendEvent(eCloseSimpleDialog);
|
||||
if (SettingsService.getProperty("serverUrls")) return;
|
||||
Navigation.navigate("PayWall", {
|
||||
context: "logged-in"
|
||||
});
|
||||
|
||||
@@ -25,6 +25,7 @@ import { DatabaseLogger, db } from "../common/database";
|
||||
import PremiumService from "../services/premium";
|
||||
import { useSettingStore } from "../stores/use-setting-store";
|
||||
import { useUserStore } from "../stores/use-user-store";
|
||||
import SettingsService from "../services/settings";
|
||||
function numberWithCommas(x: string) {
|
||||
const parts = x.toString().split(".");
|
||||
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
||||
@@ -201,7 +202,7 @@ const usePricingPlans = (options?: PricingPlansOptions) => {
|
||||
if (productId?.includes("5year")) return false;
|
||||
if (isGithubRelease) {
|
||||
if (
|
||||
user?.subscription.trialsAvailed?.some(
|
||||
user?.subscription?.trialsAvailed?.some(
|
||||
(plan) => plan === planIdToIndex(planId || currentPlan)
|
||||
)
|
||||
) {
|
||||
@@ -246,7 +247,10 @@ const usePricingPlans = (options?: PricingPlansOptions) => {
|
||||
});
|
||||
setPlans([...pricingPlans]);
|
||||
setUserCanRequestTrial(hasTrialOffer());
|
||||
if (Config.GITHUB_RELEASE === "true") {
|
||||
if (
|
||||
Config.GITHUB_RELEASE === "true" &&
|
||||
!SettingsService.getProperty("serverUrls")
|
||||
) {
|
||||
try {
|
||||
const products = WebPlanCache || (await db.pricing.products());
|
||||
WebPlanCache = products;
|
||||
@@ -264,7 +268,8 @@ const usePricingPlans = (options?: PricingPlansOptions) => {
|
||||
if (!product) return;
|
||||
|
||||
if (Platform.OS === "android") {
|
||||
if (isGithubRelease)
|
||||
if (isGithubRelease) {
|
||||
if (!(product as Plan)?.price) return null;
|
||||
return `${(product as Plan).price.currency} ${
|
||||
(product as Plan).period === "yearly"
|
||||
? ((product as Plan).price.gross / 12).toFixed(2)
|
||||
@@ -272,6 +277,7 @@ const usePricingPlans = (options?: PricingPlansOptions) => {
|
||||
? ((product as Plan).price.gross / (12 * 5)).toFixed(2)
|
||||
: (product as Plan).price.gross
|
||||
}`;
|
||||
}
|
||||
|
||||
const pricingPhaseListItem =
|
||||
(product as RNIap.SubscriptionAndroid)?.subscriptionOfferDetails?.[0]
|
||||
@@ -414,7 +420,7 @@ const usePricingPlans = (options?: PricingPlansOptions) => {
|
||||
offerIndex: number
|
||||
) => {
|
||||
if (isGithubRelease) {
|
||||
return (product as Plan).period;
|
||||
return (product as Plan)?.period;
|
||||
}
|
||||
|
||||
if (Platform.OS === "android") {
|
||||
@@ -444,7 +450,7 @@ const usePricingPlans = (options?: PricingPlansOptions) => {
|
||||
if (isGithubRelease) {
|
||||
return {
|
||||
duration: 1,
|
||||
type: (product as Plan).period
|
||||
type: (product as Plan)?.period
|
||||
};
|
||||
}
|
||||
|
||||
@@ -591,7 +597,8 @@ const usePricingPlans = (options?: PricingPlansOptions) => {
|
||||
) => {
|
||||
if (!product) return null;
|
||||
|
||||
if (isGithubRelease)
|
||||
if (isGithubRelease) {
|
||||
if (!(product as Plan)?.price) return null;
|
||||
return `${(product as Plan).price.currency} ${
|
||||
(product as Plan).period === "yearly"
|
||||
? ((product as Plan).price.gross / 12).toFixed(2)
|
||||
@@ -599,6 +606,7 @@ const usePricingPlans = (options?: PricingPlansOptions) => {
|
||||
? ((product as Plan).price.gross / (12 * 5)).toFixed(2)
|
||||
: (product as Plan).price.gross
|
||||
}`;
|
||||
}
|
||||
|
||||
const androidPricingPhase = (product as RNIap.SubscriptionAndroid)
|
||||
?.subscriptionOfferDetails?.[0].pricingPhases?.pricingPhaseList?.[
|
||||
|
||||
@@ -100,9 +100,9 @@ export const settingsGroups: SettingSection[] = [
|
||||
strings.subscriptionProviderInfo[user?.subscription?.provider];
|
||||
|
||||
const isCurrentPlatform =
|
||||
(user.subscription.provider === SubscriptionProvider.APPLE &&
|
||||
(user.subscription?.provider === SubscriptionProvider.APPLE &&
|
||||
Platform.OS === "ios") ||
|
||||
(user.subscription.provider === SubscriptionProvider.GOOGLE &&
|
||||
(user.subscription?.provider === SubscriptionProvider.GOOGLE &&
|
||||
Platform.OS === "android");
|
||||
|
||||
if (
|
||||
@@ -132,8 +132,8 @@ export const settingsGroups: SettingSection[] = [
|
||||
"dddd, MMMM D, YYYY h:mm A"
|
||||
);
|
||||
|
||||
if (user.subscription.plan !== SubscriptionPlan.FREE) {
|
||||
const status = user.subscription.status;
|
||||
if (user.subscription?.plan !== SubscriptionPlan.FREE) {
|
||||
const status = user.subscription?.status;
|
||||
return status === SubscriptionStatus.TRIAL
|
||||
? strings.trialEndsOn(
|
||||
dayjs(user?.subscription?.start)
|
||||
|
||||
@@ -24,7 +24,7 @@ import { useThemeColors } from "@notesnook/theme";
|
||||
import { useNetInfo } from "@react-native-community/netinfo";
|
||||
import dayjs from "dayjs";
|
||||
import React from "react";
|
||||
import { Image, TouchableOpacity, View } from "react-native";
|
||||
import { Image, Platform, TouchableOpacity, View } from "react-native";
|
||||
import ImagePicker from "react-native-image-crop-picker";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import { db } from "../../common/database";
|
||||
@@ -43,6 +43,7 @@ import { planToDisplayName } from "../../utils/constants";
|
||||
import { AppFontSize } from "../../utils/size";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
import { SectionItem } from "./section-item";
|
||||
import SettingsService from "../../services/settings";
|
||||
|
||||
export const getTimeLeft = (t2) => {
|
||||
let daysRemaining = dayjs(t2).diff(dayjs(), "days");
|
||||
@@ -133,9 +134,9 @@ const SettingsUserSection = ({ item }) => {
|
||||
const total = user?.totalStorage || 0;
|
||||
|
||||
const isCurrentPlatform =
|
||||
(user.subscription.provider === SubscriptionProvider.APPLE &&
|
||||
(user?.subscription?.provider === SubscriptionProvider.APPLE &&
|
||||
Platform.OS === "ios") ||
|
||||
(user.subscription.provider === SubscriptionProvider.GOOGLE &&
|
||||
(user?.subscription?.provider === SubscriptionProvider.GOOGLE &&
|
||||
Platform.OS === "android");
|
||||
|
||||
return (
|
||||
@@ -328,7 +329,7 @@ const SettingsUserSection = ({ item }) => {
|
||||
}}
|
||||
>
|
||||
<Paragraph size={AppFontSize.sm}>
|
||||
{planToDisplayName(user.subscription.plan)}
|
||||
{planToDisplayName(user.subscription?.plan)}
|
||||
</Paragraph>
|
||||
<Paragraph
|
||||
color={colors.secondary.paragraph}
|
||||
@@ -339,14 +340,16 @@ const SettingsUserSection = ({ item }) => {
|
||||
</Paragraph>
|
||||
</TouchableOpacity>
|
||||
|
||||
{(user.subscription.provider === SubscriptionProvider.PADDLE ||
|
||||
user.subscription.provider ===
|
||||
{((user.subscription?.provider ===
|
||||
SubscriptionProvider.PADDLE ||
|
||||
user.subscription?.provider ===
|
||||
SubscriptionProvider.STREETWRITERS ||
|
||||
!isCurrentPlatform) &&
|
||||
PremiumService.get() ? null : (
|
||||
PremiumService.get()) ||
|
||||
SettingsService.getProperty("serverUrls") ? null : (
|
||||
<Button
|
||||
title={
|
||||
user.subscription.plan !== SubscriptionPlan.FREE
|
||||
user.subscription?.plan !== SubscriptionPlan.FREE
|
||||
? strings.changePlan()
|
||||
: strings.upgradePlan()
|
||||
}
|
||||
@@ -362,8 +365,9 @@ const SettingsUserSection = ({ item }) => {
|
||||
}
|
||||
|
||||
if (
|
||||
user.subscription.plan !== SubscriptionPlan.FREE &&
|
||||
user.subscription.productId.includes("5year")
|
||||
user.subscription?.plan !== SubscriptionPlan.FREE &&
|
||||
user.subscription?.productId &&
|
||||
user.subscription?.productId.includes("5year")
|
||||
) {
|
||||
ToastManager.show({
|
||||
message:
|
||||
|
||||
@@ -71,6 +71,7 @@ export function planToDisplayName(plan: SubscriptionPlan): string {
|
||||
return strings.freePlan();
|
||||
case SubscriptionPlan.ESSENTIAL:
|
||||
return strings.essentialPlan();
|
||||
case SubscriptionPlan.LEGACY_PRO:
|
||||
case SubscriptionPlan.PRO:
|
||||
return strings.proPlan();
|
||||
case SubscriptionPlan.BELIEVER:
|
||||
|
||||
@@ -18,7 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { authenticator } from "otplib";
|
||||
import { Tests } from "./utils";
|
||||
import { TestBuilder } from "./utils";
|
||||
|
||||
import dotenv from "dotenv";
|
||||
import path from "path";
|
||||
@@ -34,20 +34,6 @@ const USER = {
|
||||
}
|
||||
};
|
||||
|
||||
async function login() {
|
||||
await Tests.fromText("Login to encrypt and sync notes").tap();
|
||||
await Tests.fromId("input.email").element.typeText(USER.login.email!);
|
||||
await Tests.fromText("Continue").tap();
|
||||
await Tests.sleep(3000);
|
||||
await Tests.fromId("input.totp").element.typeText(
|
||||
authenticator.generate(USER.login.totpSecret!)
|
||||
);
|
||||
await Tests.fromText("Next").tap();
|
||||
await Tests.sleep(3000);
|
||||
await Tests.fromId("input.password").element.typeText(USER.login.password!);
|
||||
await Tests.fromId("input.password").element.tapReturnKey();
|
||||
}
|
||||
|
||||
// async function deleteAccount() {
|
||||
// await tapByText("Account Settings");
|
||||
// await sleep(2000);
|
||||
@@ -67,11 +53,27 @@ async function login() {
|
||||
// await elementById("input.confirmPassword").tapReturnKey();
|
||||
// }
|
||||
|
||||
async function login() {
|
||||
await TestBuilder.create()
|
||||
.waitAndTapByText("Login to encrypt and sync notes")
|
||||
.typeTextById("input.email", USER.login.email!)
|
||||
.tapReturnKeyById("input.email")
|
||||
.wait(3000)
|
||||
.typeTextById("input.totp", authenticator.generate(USER.login.totpSecret!))
|
||||
.waitAndTapByText("Next")
|
||||
.wait(3000)
|
||||
.typeTextById("input.password", USER.login.password!)
|
||||
.tapReturnKeyById("input.password")
|
||||
.run();
|
||||
}
|
||||
|
||||
describe("AUTH", () => {
|
||||
it("Login", async () => {
|
||||
await Tests.prepare();
|
||||
await login();
|
||||
await Tests.sleep(10000);
|
||||
await Tests.fromText("Login to encrypt and sync notes").isNotVisible();
|
||||
await TestBuilder.create()
|
||||
.prepare()
|
||||
.addStep(login)
|
||||
.wait(3000)
|
||||
.isNotVisibleByText("Notesnook Plans")
|
||||
.run();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { notesnook } from "../test.ids";
|
||||
import { TestBuilder } from "./utils";
|
||||
|
||||
|
||||
@@ -18,266 +18,209 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { notesnook } from "../test.ids";
|
||||
import { Tests } from "./utils";
|
||||
import { TestBuilder } from "./utils";
|
||||
|
||||
describe("NOTEBOOKS", () => {
|
||||
it("Create a notebook with title only", async () => {
|
||||
await Tests.prepare();
|
||||
await Tests.openSideMenu();
|
||||
await Tests.fromId("tab-notebooks").waitAndTap();
|
||||
await Tests.fromId("sidebar-add-button").waitAndTap();
|
||||
await Tests.createNotebook("Notebook 1", false);
|
||||
await Tests.sleep(500);
|
||||
await Tests.fromText("Notebook 1").isVisible();
|
||||
await TestBuilder.create()
|
||||
.prepare()
|
||||
.openSideMenu()
|
||||
.waitAndTapById("tab-notebooks")
|
||||
.waitAndTapById("sidebar-add-button")
|
||||
.createNotebook("Notebook 1", false)
|
||||
.wait(500)
|
||||
.isVisibleByText("Notebook 1")
|
||||
.run();
|
||||
});
|
||||
|
||||
it("Create a notebook title & description", async () => {
|
||||
await Tests.prepare();
|
||||
await Tests.openSideMenu();
|
||||
await Tests.fromId("tab-notebooks").waitAndTap();
|
||||
await Tests.fromId("sidebar-add-button").waitAndTap();
|
||||
await Tests.createNotebook("Notebook 1", true);
|
||||
await Tests.sleep(500);
|
||||
await Tests.fromText("Notebook 1").isVisible();
|
||||
it("Create a notebook with title & description", async () => {
|
||||
await TestBuilder.create()
|
||||
.prepare()
|
||||
.openSideMenu()
|
||||
.waitAndTapById("tab-notebooks")
|
||||
.waitAndTapById("sidebar-add-button")
|
||||
.createNotebook("Notebook 1", true)
|
||||
.wait(500)
|
||||
.isVisibleByText("Notebook 1")
|
||||
.run();
|
||||
});
|
||||
|
||||
it("Add a sub notebook to a notebook", async () => {
|
||||
await Tests.prepare();
|
||||
await Tests.openSideMenu();
|
||||
await Tests.fromId("tab-notebooks").waitAndTap();
|
||||
await Tests.fromId("sidebar-add-button").waitAndTap();
|
||||
await Tests.createNotebook("Notebook 1", true);
|
||||
await Tests.sleep(500);
|
||||
await Tests.fromText("Notebook 1").element.longPress();
|
||||
await Tests.sleep(500);
|
||||
await Tests.fromText("Add notebook").waitAndTap();
|
||||
await Tests.createNotebook("Sub notebook", true);
|
||||
await Tests.sleep(500);
|
||||
await Tests.fromId("expand-notebook-0").waitAndTap();
|
||||
await Tests.fromText("Sub notebook").isVisible();
|
||||
await Tests.fromText("Sub notebook").element.longPress();
|
||||
await Tests.sleep(500);
|
||||
await Tests.fromText("Move to trash").waitAndTap();
|
||||
await Tests.fromText("Delete").waitAndTap();
|
||||
await Tests.fromText("Sub notebook").isNotVisible();
|
||||
await TestBuilder.create()
|
||||
.prepare()
|
||||
.openSideMenu()
|
||||
.waitAndTapById("tab-notebooks")
|
||||
.waitAndTapById("sidebar-add-button")
|
||||
.createNotebook("Notebook 1", true)
|
||||
.wait(500)
|
||||
.longPressByText("Notebook 1")
|
||||
.wait(500)
|
||||
.waitAndTapByText("Add notebook")
|
||||
.createNotebook("Sub notebook", true)
|
||||
.wait(500)
|
||||
.waitAndTapById("expand-notebook-0")
|
||||
.isVisibleByText("Sub notebook")
|
||||
.longPressByText("Sub notebook")
|
||||
.wait(500)
|
||||
.waitAndTapByText("Move to trash")
|
||||
.waitAndTapByText("Delete")
|
||||
.isNotVisibleByText("Sub notebook")
|
||||
.run();
|
||||
});
|
||||
|
||||
it("Edit notebook", async () => {
|
||||
await Tests.prepare();
|
||||
await Tests.openSideMenu();
|
||||
await Tests.fromId("tab-notebooks").waitAndTap();
|
||||
await Tests.fromId("sidebar-add-button").waitAndTap();
|
||||
await Tests.createNotebook("Notebook 1", true);
|
||||
await Tests.sleep(500);
|
||||
await Tests.fromText("Notebook 1").element.longPress();
|
||||
await Tests.sleep(500);
|
||||
await Tests.fromText("Edit notebook").waitAndTap();
|
||||
await Tests.fromId(
|
||||
notesnook.ids.dialogs.notebook.inputs.title
|
||||
).element.typeText(" (edited)");
|
||||
await Tests.fromText("Save").waitAndTap();
|
||||
await Tests.fromText("Notebook 1 (edited)").isVisible();
|
||||
await TestBuilder.create()
|
||||
.prepare()
|
||||
.openSideMenu()
|
||||
.waitAndTapById("tab-notebooks")
|
||||
.waitAndTapById("sidebar-add-button")
|
||||
.createNotebook("Notebook 1", true)
|
||||
.wait(500)
|
||||
.longPressByText("Notebook 1")
|
||||
.wait(500)
|
||||
.waitAndTapByText("Edit notebook")
|
||||
.typeTextById(notesnook.ids.dialogs.notebook.inputs.title, " (edited)")
|
||||
.waitAndTapByText("Save")
|
||||
.isVisibleByText("Notebook 1 (edited)")
|
||||
.run();
|
||||
});
|
||||
|
||||
it("Edit a sub notebook", async () => {
|
||||
await Tests.prepare();
|
||||
|
||||
await Tests.openSideMenu();
|
||||
await Tests.fromId("tab-notebooks").waitAndTap();
|
||||
await Tests.fromId("sidebar-add-button").waitAndTap();
|
||||
await Tests.createNotebook("Notebook 1", true);
|
||||
await Tests.sleep(500);
|
||||
|
||||
await Tests.fromText("Notebook 1").element.longPress();
|
||||
await Tests.sleep(500);
|
||||
await Tests.fromText("Add notebook").waitAndTap();
|
||||
|
||||
await Tests.createNotebook("Sub notebook", true);
|
||||
await Tests.sleep(500);
|
||||
|
||||
await Tests.fromId("expand-notebook-0").waitAndTap();
|
||||
await Tests.fromText("Sub notebook").element.longPress();
|
||||
await Tests.sleep(500);
|
||||
await Tests.fromText("Edit notebook").waitAndTap();
|
||||
await Tests.fromId(
|
||||
notesnook.ids.dialogs.notebook.inputs.title
|
||||
).element.typeText(" (edited)");
|
||||
await Tests.fromText("Save").waitAndTap();
|
||||
await Tests.fromText("Sub notebook (edited)").isVisible();
|
||||
await TestBuilder.create()
|
||||
.prepare()
|
||||
.openSideMenu()
|
||||
.waitAndTapById("tab-notebooks")
|
||||
.waitAndTapById("sidebar-add-button")
|
||||
.createNotebook("Notebook 1", true)
|
||||
.wait(500)
|
||||
.longPressByText("Notebook 1")
|
||||
.wait(500)
|
||||
.waitAndTapByText("Add notebook")
|
||||
.createNotebook("Sub notebook", true)
|
||||
.wait(500)
|
||||
.waitAndTapById("expand-notebook-0")
|
||||
.longPressByText("Sub notebook")
|
||||
.wait(500)
|
||||
.waitAndTapByText("Edit notebook")
|
||||
.typeTextById(notesnook.ids.dialogs.notebook.inputs.title, " (edited)")
|
||||
.waitAndTapByText("Save")
|
||||
.isVisibleByText("Sub notebook (edited)")
|
||||
.run();
|
||||
});
|
||||
|
||||
it("Add a note to notebook", async () => {
|
||||
await Tests.prepare();
|
||||
|
||||
await Tests.openSideMenu();
|
||||
await Tests.fromId("tab-notebooks").waitAndTap();
|
||||
await Tests.fromId("sidebar-add-button").waitAndTap();
|
||||
|
||||
await Tests.createNotebook("Notebook 1", true);
|
||||
await Tests.sleep(500);
|
||||
|
||||
await Tests.fromText("Notebook 1").waitAndTap();
|
||||
await Tests.createNote();
|
||||
await TestBuilder.create()
|
||||
.prepare()
|
||||
.openSideMenu()
|
||||
.waitAndTapById("tab-notebooks")
|
||||
.waitAndTapById("sidebar-add-button")
|
||||
.createNotebook("Notebook 1", true)
|
||||
.wait(500)
|
||||
.waitAndTapByText("Notebook 1")
|
||||
.createNote()
|
||||
.run();
|
||||
});
|
||||
|
||||
it("Remove note from Notebook", async () => {
|
||||
await Tests.prepare();
|
||||
|
||||
await Tests.openSideMenu();
|
||||
await Tests.fromId("tab-notebooks").waitAndTap();
|
||||
await Tests.fromId("sidebar-add-button").waitAndTap();
|
||||
|
||||
await Tests.createNotebook("Notebook 1", true);
|
||||
await Tests.sleep(500);
|
||||
|
||||
await Tests.fromText("Notebook 1").waitAndTap();
|
||||
await Tests.sleep(500);
|
||||
let note = await Tests.createNote();
|
||||
await Tests.fromText(note.body).element.longPress();
|
||||
await Tests.sleep(500);
|
||||
|
||||
await Tests.fromId("select-minus").waitAndTap();
|
||||
await Tests.fromId(note.title).isNotVisible();
|
||||
it.only("Remove note from notebook", async () => {
|
||||
await TestBuilder.create()
|
||||
.prepare()
|
||||
.openSideMenu()
|
||||
.waitAndTapById("tab-notebooks")
|
||||
.waitAndTapById("sidebar-add-button")
|
||||
.createNotebook("Notebook 1", true)
|
||||
.wait(500)
|
||||
.waitAndTapByText("Notebook 1")
|
||||
.wait(500)
|
||||
.createNote()
|
||||
.saveResult()
|
||||
.processResult(async (note) => {
|
||||
await TestBuilder.create()
|
||||
.longPressByText(note.body)
|
||||
.wait(500)
|
||||
.waitAndTapById("select-minus")
|
||||
.isNotVisibleById(note.title)
|
||||
.run();
|
||||
})
|
||||
.run();
|
||||
});
|
||||
|
||||
it("Add/Remove note to notebook from home", async () => {
|
||||
await Tests.prepare();
|
||||
|
||||
await Tests.openSideMenu();
|
||||
await Tests.fromId("tab-notebooks").waitAndTap();
|
||||
await Tests.fromId("sidebar-add-button").waitAndTap();
|
||||
|
||||
await Tests.createNotebook("Notebook 1", true);
|
||||
await Tests.sleep(500);
|
||||
await Tests.fromId("tab-home").waitAndTap();
|
||||
|
||||
await Tests.fromText("Notes").waitAndTap();
|
||||
await Tests.createNote();
|
||||
|
||||
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
|
||||
|
||||
await Tests.sleep(500);
|
||||
|
||||
await Tests.fromId("icon-notebooks").waitAndTap();
|
||||
await Tests.fromText("Notebook 1").waitAndTap();
|
||||
|
||||
await Tests.fromId("floating-save-button").waitAndTap();
|
||||
await Tests.fromText("Notebook 1").isVisible();
|
||||
await TestBuilder.create()
|
||||
.prepare()
|
||||
.openSideMenu()
|
||||
.waitAndTapById("tab-notebooks")
|
||||
.waitAndTapById("sidebar-add-button")
|
||||
.createNotebook("Notebook 1", true)
|
||||
.wait(500)
|
||||
.waitAndTapById("tab-home")
|
||||
.waitAndTapByText("Notes")
|
||||
.createNote()
|
||||
.waitAndTapById(notesnook.listitem.menu)
|
||||
.wait(500)
|
||||
.waitAndTapById("icon-notebooks")
|
||||
.waitAndTapByText("Notebook 1")
|
||||
.waitAndTapById("floating-save-button")
|
||||
.isVisibleByText("Notebook 1")
|
||||
.run();
|
||||
});
|
||||
|
||||
it("Edit notebook title, description", async () => {
|
||||
await Tests.prepare();
|
||||
|
||||
await Tests.openSideMenu();
|
||||
await Tests.fromId("tab-notebooks").waitAndTap();
|
||||
await Tests.fromId("sidebar-add-button").waitAndTap();
|
||||
|
||||
await Tests.createNotebook();
|
||||
|
||||
await Tests.sleep(500);
|
||||
|
||||
await Tests.fromText("Notebook 1").isVisible();
|
||||
|
||||
await Tests.fromText("Notebook 1").element.longPress();
|
||||
|
||||
await Tests.sleep(500);
|
||||
await Tests.fromText("Edit notebook").waitAndTap();
|
||||
await Tests.fromId(
|
||||
notesnook.ids.dialogs.notebook.inputs.title
|
||||
).element.typeText(" (Edited)");
|
||||
await Tests.fromId(
|
||||
notesnook.ids.dialogs.notebook.inputs.description
|
||||
).element.clearText();
|
||||
await Tests.fromId(
|
||||
notesnook.ids.dialogs.notebook.inputs.description
|
||||
).element.typeText("Description of Notebook 1 (Edited)");
|
||||
await Tests.fromText("Save").waitAndTap();
|
||||
await Tests.fromText("Notebook 1 (Edited)").isVisible();
|
||||
it("Edit notebook title and description", async () => {
|
||||
await TestBuilder.create()
|
||||
.prepare()
|
||||
.openSideMenu()
|
||||
.waitAndTapById("tab-notebooks")
|
||||
.waitAndTapById("sidebar-add-button")
|
||||
.createNotebook()
|
||||
.wait(500)
|
||||
.isVisibleByText("Notebook 1")
|
||||
.longPressByText("Notebook 1")
|
||||
.wait(500)
|
||||
.waitAndTapByText("Edit notebook")
|
||||
.typeTextById(notesnook.ids.dialogs.notebook.inputs.title, " (Edited)")
|
||||
.clearTextById(notesnook.ids.dialogs.notebook.inputs.description)
|
||||
.typeTextById(
|
||||
notesnook.ids.dialogs.notebook.inputs.description,
|
||||
"Description of Notebook 1 (Edited)"
|
||||
)
|
||||
.waitAndTapByText("Save")
|
||||
.isVisibleByText("Notebook 1 (Edited)")
|
||||
.run();
|
||||
});
|
||||
|
||||
it("Move notebook to trash", async () => {
|
||||
await Tests.prepare();
|
||||
|
||||
await Tests.openSideMenu();
|
||||
await Tests.fromId("tab-notebooks").waitAndTap();
|
||||
await Tests.fromId("sidebar-add-button").waitAndTap();
|
||||
|
||||
await Tests.createNotebook("Notebook 1", false);
|
||||
await Tests.sleep(500);
|
||||
await Tests.fromText("Notebook 1").isVisible();
|
||||
|
||||
await Tests.fromText("Notebook 1").element.longPress();
|
||||
|
||||
await Tests.sleep(500);
|
||||
await Tests.fromText("Move to trash").waitAndTap();
|
||||
await Tests.fromText("Delete").waitAndTap();
|
||||
|
||||
await Tests.fromId("tab-home").waitAndTap();
|
||||
|
||||
await Tests.fromText("Trash").waitAndTap();
|
||||
await Tests.fromText("Notebook 1").isVisible();
|
||||
});
|
||||
|
||||
it.skip("Move notebook to trash with notes", async () => {
|
||||
await Tests.prepare();
|
||||
let note = await Tests.createNote();
|
||||
|
||||
await Tests.openSideMenu();
|
||||
await Tests.fromId("tab-notebooks").waitAndTap();
|
||||
await Tests.fromId("sidebar-add-button").waitAndTap();
|
||||
|
||||
await Tests.createNotebook("Notebook 1", false);
|
||||
await device.pressBack();
|
||||
await Tests.fromId(notesnook.ids.note.get(0)).element.longPress();
|
||||
await Tests.sleep(500);
|
||||
await Tests.fromId("select-plus").waitAndTap();
|
||||
await Tests.fromText("Notebook 1").isVisible();
|
||||
await Tests.fromText("Notebook 1").waitAndTap();
|
||||
await Tests.fromId("floating-save-button").waitAndTap();
|
||||
await Tests.sleep(500);
|
||||
await Tests.openSideMenu();
|
||||
await Tests.fromId("tab-home").waitAndTap();
|
||||
await Tests.fromId("tab-notebooks").waitAndTap();
|
||||
await Tests.sleep(500);
|
||||
await Tests.fromText("Notebook 1").isVisible();
|
||||
await Tests.fromText("Notebook 1").element.longPress();
|
||||
await Tests.sleep(500);
|
||||
await Tests.fromText("Move to trash").waitAndTap();
|
||||
await Tests.fromText(
|
||||
"Move all notes in this notebook to trash"
|
||||
).waitAndTap();
|
||||
await Tests.fromText("Delete").waitAndTap();
|
||||
await Tests.sleep(500);
|
||||
await Tests.fromId("tab-home").waitAndTap();
|
||||
|
||||
await Tests.fromText("Trash").waitAndTap();
|
||||
|
||||
await Tests.fromText("Notebook 1").isVisible();
|
||||
await Tests.fromText(note.body).isVisible();
|
||||
await TestBuilder.create()
|
||||
.prepare()
|
||||
.openSideMenu()
|
||||
.waitAndTapById("tab-notebooks")
|
||||
.waitAndTapById("sidebar-add-button")
|
||||
.createNotebook("Notebook 1", false)
|
||||
.wait(500)
|
||||
.isVisibleByText("Notebook 1")
|
||||
.longPressByText("Notebook 1")
|
||||
.wait(500)
|
||||
.waitAndTapByText("Move to trash")
|
||||
.waitAndTapByText("Delete")
|
||||
.waitAndTapById("tab-home")
|
||||
.waitAndTapByText("Trash")
|
||||
.isVisibleByText("Notebook 1")
|
||||
.run();
|
||||
});
|
||||
|
||||
it("Pin notebook to side menu", async () => {
|
||||
await Tests.prepare();
|
||||
|
||||
await Tests.openSideMenu();
|
||||
await Tests.fromId("tab-notebooks").waitAndTap();
|
||||
await Tests.fromId("sidebar-add-button").waitAndTap();
|
||||
|
||||
await Tests.createNotebook("Notebook 1", false);
|
||||
|
||||
await Tests.sleep(500);
|
||||
|
||||
await Tests.fromText("Notebook 1").isVisible();
|
||||
|
||||
await Tests.fromText("Notebook 1").element.longPress();
|
||||
|
||||
await Tests.sleep(500);
|
||||
|
||||
await Tests.fromId("icon-add-shortcut").waitAndTap();
|
||||
|
||||
await Tests.sleep(500);
|
||||
await Tests.fromId("tab-home").waitAndTap();
|
||||
|
||||
await Tests.fromText("Notebook 1").isVisible();
|
||||
await TestBuilder.create()
|
||||
.prepare()
|
||||
.openSideMenu()
|
||||
.waitAndTapById("tab-notebooks")
|
||||
.waitAndTapById("sidebar-add-button")
|
||||
.createNotebook("Notebook 1", false)
|
||||
.wait(500)
|
||||
.isVisibleByText("Notebook 1")
|
||||
.longPressByText("Notebook 1")
|
||||
.wait(500)
|
||||
.waitAndTapById("icon-add-shortcut")
|
||||
.wait(500)
|
||||
.waitAndTapById("tab-home")
|
||||
.isVisibleByText("Notebook 1")
|
||||
.run();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -17,15 +17,17 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Tests } from "./utils";
|
||||
import { TestBuilder } from "./utils";
|
||||
|
||||
describe("Search", () => {
|
||||
it("Search for a note", async () => {
|
||||
await Tests.prepare();
|
||||
let note = await Tests.createNote();
|
||||
await Tests.fromId("search-header").waitAndTap();
|
||||
await Tests.fromId("search-input").element.typeText("Test");
|
||||
await Tests.sleep(1000);
|
||||
await Tests.fromText("1").isVisible();
|
||||
await TestBuilder.create()
|
||||
.prepare()
|
||||
.createNote()
|
||||
.waitAndTapById("search-header")
|
||||
.typeTextById("search-input", "Test")
|
||||
.wait(1000)
|
||||
.isVisibleByText("1")
|
||||
.run();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -18,74 +18,99 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { notesnook } from "../test.ids";
|
||||
import { Tests } from "./utils";
|
||||
|
||||
async function sortBy(sorting: string) {
|
||||
await Tests.fromId("icon-sort").waitAndTap();
|
||||
await Tests.sleep(500);
|
||||
await Tests.fromText(sorting).waitAndTap();
|
||||
await device.pressBack();
|
||||
}
|
||||
import { TestBuilder } from "./utils";
|
||||
|
||||
describe("Sort & filter", () => {
|
||||
it("Sort by date-edited/date-created", async () => {
|
||||
await Tests.prepare();
|
||||
let webview = web();
|
||||
await Tests.createNote("Note 1", "Note 1");
|
||||
await Tests.createNote("Note 2", "Note 2");
|
||||
await Tests.fromText("Note 1").waitAndTap();
|
||||
await webview.element(by.web.className("ProseMirror")).tap();
|
||||
await webview
|
||||
.element(by.web.className("ProseMirror"))
|
||||
.typeText("Edited ", true);
|
||||
await device.pressBack();
|
||||
await sortBy("Date created");
|
||||
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
|
||||
await device.pressBack();
|
||||
await sortBy("Date edited");
|
||||
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
|
||||
await Tests.sleep(500);
|
||||
await device.pressBack();
|
||||
await TestBuilder.create()
|
||||
.prepare()
|
||||
.createNote("Note 1", "Note 1")
|
||||
.createNote("Note 2", "Note 2")
|
||||
.waitAndTapByText("Note 1")
|
||||
.addStep(async () => {
|
||||
const webview = web();
|
||||
await webview.element(by.web.className("ProseMirror")).tap();
|
||||
await webview
|
||||
.element(by.web.className("ProseMirror"))
|
||||
.typeText("Edited ", true);
|
||||
})
|
||||
.pressBack()
|
||||
.waitAndTapById("icon-sort")
|
||||
.wait(500)
|
||||
.waitAndTapByText("Date created")
|
||||
.pressBack()
|
||||
.waitAndTapById(notesnook.listitem.menu)
|
||||
.pressBack()
|
||||
.waitAndTapById("icon-sort")
|
||||
.wait(500)
|
||||
.waitAndTapByText("Date edited")
|
||||
.pressBack()
|
||||
.waitAndTapById(notesnook.listitem.menu)
|
||||
.run();
|
||||
});
|
||||
|
||||
it("Disable grouping", async () => {
|
||||
await Tests.prepare();
|
||||
await Tests.createNote("Note 1", "Note 1");
|
||||
await sortBy("None");
|
||||
await Tests.fromText("ALL").isVisible();
|
||||
await TestBuilder.create()
|
||||
.prepare()
|
||||
.createNote("Note 1", "Note 1")
|
||||
.waitAndTapById("icon-sort")
|
||||
.wait(500)
|
||||
.waitAndTapByText("None")
|
||||
.pressBack()
|
||||
.isVisibleByText("ALL")
|
||||
.run();
|
||||
});
|
||||
|
||||
it("Group by Abc", async () => {
|
||||
await Tests.prepare();
|
||||
await Tests.createNote("Note 1", "Note 1");
|
||||
await sortBy("Abc");
|
||||
await Tests.fromText("N").isVisible();
|
||||
await TestBuilder.create()
|
||||
.prepare()
|
||||
.createNote("Note 1", "Note 1")
|
||||
.waitAndTapById("icon-sort")
|
||||
.wait(500)
|
||||
.waitAndTapByText("Abc")
|
||||
.pressBack()
|
||||
.isVisibleByText("N")
|
||||
.run();
|
||||
});
|
||||
|
||||
it("Group by Year", async () => {
|
||||
await Tests.prepare();
|
||||
await Tests.createNote("Note 1", "Note 1");
|
||||
await sortBy("Year");
|
||||
await TestBuilder.create()
|
||||
.prepare()
|
||||
.createNote("Note 1", "Note 1")
|
||||
.waitAndTapById("icon-sort")
|
||||
.wait(500)
|
||||
.waitAndTapByText("Year")
|
||||
.run();
|
||||
});
|
||||
|
||||
it("Group by Week", async () => {
|
||||
await Tests.prepare();
|
||||
await Tests.createNote("Note 1", "Note 1");
|
||||
await sortBy("Week");
|
||||
await TestBuilder.create()
|
||||
.prepare()
|
||||
.createNote("Note 1", "Note 1")
|
||||
.waitAndTapById("icon-sort")
|
||||
.wait(500)
|
||||
.waitAndTapByText("Week")
|
||||
.run();
|
||||
});
|
||||
|
||||
it("Group by Month", async () => {
|
||||
await Tests.prepare();
|
||||
await Tests.createNote("Note 1", "Note 1");
|
||||
await sortBy("Month");
|
||||
await TestBuilder.create()
|
||||
.prepare()
|
||||
.createNote("Note 1", "Note 1")
|
||||
.waitAndTapById("icon-sort")
|
||||
.wait(500)
|
||||
.waitAndTapByText("Month")
|
||||
.run();
|
||||
});
|
||||
|
||||
it("Compact mode", async () => {
|
||||
await Tests.prepare();
|
||||
await Tests.createNote("Note 1", "Note 1");
|
||||
await Tests.fromId("icon-compact-mode").waitAndTap();
|
||||
await Tests.fromText("Note 1").isNotVisible();
|
||||
await Tests.fromId("icon-compact-mode").waitAndTap();
|
||||
await Tests.fromText("Note 1").isVisible();
|
||||
await TestBuilder.create()
|
||||
.prepare()
|
||||
.createNote("Note 1", "Note 1")
|
||||
.waitAndTapById("icon-compact-mode")
|
||||
.isNotVisibleByText("Note 1")
|
||||
.waitAndTapById("icon-compact-mode")
|
||||
.isVisibleByText("Note 1")
|
||||
.run();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -18,95 +18,101 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { notesnook } from "../test.ids";
|
||||
import { Tests } from "./utils";
|
||||
import { TestBuilder } from "./utils";
|
||||
|
||||
describe("Tags", () => {
|
||||
it("Create a tag", async () => {
|
||||
await Tests.prepare();
|
||||
await Tests.openSideMenu();
|
||||
await Tests.fromId("tab-tags").waitAndTap();
|
||||
|
||||
await Tests.fromText("No tags").isVisible();
|
||||
await Tests.fromId("sidebar-add-button").waitAndTap();
|
||||
|
||||
await Tests.fromId("input-value").element.typeText("testtag");
|
||||
|
||||
await Tests.fromText("Add").waitAndTap();
|
||||
await Tests.fromText("testtag").isVisible();
|
||||
await TestBuilder.create()
|
||||
.prepare()
|
||||
.openSideMenu()
|
||||
.waitAndTapById("tab-tags")
|
||||
.isVisibleByText("No tags")
|
||||
.waitAndTapById("sidebar-add-button")
|
||||
.typeTextById("input-value", "testtag")
|
||||
.waitAndTapByText("Add")
|
||||
.isVisibleByText("testtag")
|
||||
.run();
|
||||
});
|
||||
|
||||
it("Tag a note", async () => {
|
||||
await Tests.prepare();
|
||||
let note = await Tests.createNote();
|
||||
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
|
||||
await Tests.sleep(500);
|
||||
await Tests.fromText("Add tag").waitAndTap();
|
||||
await Tests.fromId("tag-input").element.typeText("testtag");
|
||||
await Tests.fromText('Add "#testtag"').waitAndTap();
|
||||
await Tests.fromText("#testtag").isVisible();
|
||||
await device.pressBack();
|
||||
await device.pressBack();
|
||||
await Tests.openSideMenu();
|
||||
await Tests.fromId("tab-tags").waitAndTap();
|
||||
await Tests.fromText("#testtag").waitAndTap();
|
||||
await Tests.fromText(note.body).isVisible();
|
||||
await TestBuilder.create()
|
||||
.prepare()
|
||||
.createNote()
|
||||
.saveResult()
|
||||
.waitAndTapById(notesnook.listitem.menu)
|
||||
.wait(500)
|
||||
.waitAndTapByText("Add tag")
|
||||
.typeTextById("tag-input", "testtag")
|
||||
.waitAndTapByText('Add "#testtag"')
|
||||
.isVisibleByText("#testtag")
|
||||
.pressBack(2)
|
||||
.openSideMenu()
|
||||
.waitAndTapById("tab-tags")
|
||||
.waitAndTapByText("#testtag")
|
||||
.processResult(async (note) => {
|
||||
await TestBuilder.create().isVisibleByText(note.body).run();
|
||||
})
|
||||
.run();
|
||||
});
|
||||
|
||||
it("Untag a note", async () => {
|
||||
await Tests.prepare();
|
||||
await Tests.createNote();
|
||||
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
|
||||
await Tests.sleep(500);
|
||||
await Tests.fromText("Add tag").waitAndTap();
|
||||
await Tests.fromId("tag-input").element.typeText("testtag");
|
||||
await Tests.fromText('Add "#testtag"').waitAndTap();
|
||||
await Tests.fromText("#testtag").isVisible();
|
||||
await Tests.fromText("#testtag").waitAndTap();
|
||||
await device.pressBack();
|
||||
await device.pressBack();
|
||||
await Tests.fromText("#testtag").isNotVisible();
|
||||
await TestBuilder.create()
|
||||
.prepare()
|
||||
.createNote()
|
||||
.waitAndTapById(notesnook.listitem.menu)
|
||||
.wait(500)
|
||||
.waitAndTapByText("Add tag")
|
||||
.typeTextById("tag-input", "testtag")
|
||||
.waitAndTapByText('Add "#testtag"')
|
||||
.isVisibleByText("#testtag")
|
||||
.waitAndTapByText("#testtag")
|
||||
.pressBack(2)
|
||||
.isNotVisibleByText("#testtag")
|
||||
.run();
|
||||
});
|
||||
|
||||
it("Create shortcut of a tag", async () => {
|
||||
await Tests.prepare();
|
||||
await Tests.createNote();
|
||||
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
|
||||
await Tests.sleep(500);
|
||||
await Tests.fromText("Add tag").waitAndTap();
|
||||
await Tests.fromId("tag-input").element.typeText("testtag");
|
||||
await Tests.fromText('Add "#testtag"').waitAndTap();
|
||||
await Tests.fromText("#testtag").isVisible();
|
||||
await device.pressBack();
|
||||
await device.pressBack();
|
||||
await Tests.openSideMenu();
|
||||
await Tests.fromId("tab-tags").waitAndTap();
|
||||
await Tests.fromText("testtag").element.longPress();
|
||||
await Tests.sleep(500);
|
||||
await Tests.fromText("Add shortcut").waitAndTap();
|
||||
await Tests.fromId("tab-home").waitAndTap();
|
||||
await Tests.fromText("testtag").isVisible();
|
||||
await TestBuilder.create()
|
||||
.prepare()
|
||||
.createNote()
|
||||
.waitAndTapById(notesnook.listitem.menu)
|
||||
.wait(500)
|
||||
.waitAndTapByText("Add tag")
|
||||
.typeTextById("tag-input", "testtag")
|
||||
.waitAndTapByText('Add "#testtag"')
|
||||
.isVisibleByText("#testtag")
|
||||
.pressBack(2)
|
||||
.openSideMenu()
|
||||
.waitAndTapById("tab-tags")
|
||||
.longPressByText("testtag")
|
||||
.wait(500)
|
||||
.waitAndTapByText("Add shortcut")
|
||||
.waitAndTapById("tab-home")
|
||||
.isVisibleByText("testtag")
|
||||
.run();
|
||||
});
|
||||
|
||||
it("Rename a tag", async () => {
|
||||
await Tests.prepare();
|
||||
await Tests.createNote();
|
||||
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
|
||||
await Tests.sleep(500);
|
||||
await Tests.fromText("Add tag").waitAndTap();
|
||||
await Tests.fromId("tag-input").element.typeText("testtag");
|
||||
await Tests.fromText('Add "#testtag"').waitAndTap();
|
||||
await Tests.fromText("#testtag").isVisible();
|
||||
await device.pressBack();
|
||||
await device.pressBack();
|
||||
await Tests.openSideMenu();
|
||||
await Tests.fromId("tab-tags").waitAndTap();
|
||||
await Tests.fromText("testtag").element.longPress();
|
||||
await Tests.sleep(500);
|
||||
await Tests.fromText("Rename").waitAndTap();
|
||||
await Tests.sleep(100);
|
||||
await Tests.fromId("input-value").element.clearText();
|
||||
await Tests.fromId("input-value").element.typeText("testtag_edited");
|
||||
await Tests.fromText("Save").waitAndTap();
|
||||
await Tests.fromText("testtag_edited").isVisible();
|
||||
await TestBuilder.create()
|
||||
.prepare()
|
||||
.createNote()
|
||||
.waitAndTapById(notesnook.listitem.menu)
|
||||
.wait(500)
|
||||
.waitAndTapByText("Add tag")
|
||||
.typeTextById("tag-input", "testtag")
|
||||
.waitAndTapByText('Add "#testtag"')
|
||||
.isVisibleByText("#testtag")
|
||||
.pressBack(2)
|
||||
.openSideMenu()
|
||||
.waitAndTapById("tab-tags")
|
||||
.longPressByText("testtag")
|
||||
.wait(500)
|
||||
.waitAndTapByText("Rename")
|
||||
.wait(100)
|
||||
.clearTextById("input-value")
|
||||
.typeTextById("input-value", "testtag_edited")
|
||||
.waitAndTapByText("Save")
|
||||
.isVisibleByText("testtag_edited")
|
||||
.run();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -171,7 +171,7 @@ class TestBuilder {
|
||||
});
|
||||
}
|
||||
|
||||
wait(duration: number) {
|
||||
wait(duration = 500) {
|
||||
return this.addStep(async () => {
|
||||
await Tests.sleep(duration);
|
||||
});
|
||||
@@ -280,6 +280,13 @@ class TestBuilder {
|
||||
});
|
||||
}
|
||||
|
||||
tapReturnKeyById(id: string) {
|
||||
return this.addStep(async () => {
|
||||
const element = new Element("id", id);
|
||||
await element.element.tapReturnKey();
|
||||
});
|
||||
}
|
||||
|
||||
tapByText(text: string, point?: Detox.Point2D) {
|
||||
return this.addStep(async () => {
|
||||
const element = new Element("text", text);
|
||||
@@ -303,6 +310,12 @@ class TestBuilder {
|
||||
});
|
||||
}
|
||||
|
||||
clearTextById(id: string) {
|
||||
return this.addStep(async () => {
|
||||
await Element.fromId(id).element.clearText();
|
||||
});
|
||||
}
|
||||
|
||||
pressBack(count = 1) {
|
||||
return this.addStep(async () => {
|
||||
for (let i = 0; i < count; i++) {
|
||||
@@ -311,6 +324,20 @@ class TestBuilder {
|
||||
});
|
||||
}
|
||||
|
||||
longPressByText(text: string) {
|
||||
return this.addStep(async () => {
|
||||
const element = new Element("text", text);
|
||||
await element.element.longPress();
|
||||
});
|
||||
}
|
||||
|
||||
longPressById(id: string) {
|
||||
return this.addStep(async () => {
|
||||
const element = new Element("id", id);
|
||||
await element.element.longPress();
|
||||
});
|
||||
}
|
||||
|
||||
async run() {
|
||||
for (const step of this.steps) {
|
||||
const result = step.call(this);
|
||||
|
||||
@@ -18,134 +18,133 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import { expect } from "detox";
|
||||
import { notesnook } from "../test.ids";
|
||||
import { Tests } from "./utils";
|
||||
import { TestBuilder, Tests } from "./utils";
|
||||
|
||||
async function lockNote() {
|
||||
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
|
||||
await Tests.sleep(500);
|
||||
await Tests.fromId("icon-lock-unlock").waitAndTap();
|
||||
await Tests.sleep(500);
|
||||
await Tests.fromText("Lock").isVisible();
|
||||
await Tests.fromId(notesnook.ids.dialogs.vault.pwd).element.typeText("1234");
|
||||
await Tests.fromId(notesnook.ids.dialogs.vault.pwdAlt).element.typeText(
|
||||
"1234"
|
||||
);
|
||||
await Tests.fromText("Lock").waitAndTap();
|
||||
await Tests.fromId("note-locked-icon").isVisible();
|
||||
await TestBuilder.create()
|
||||
.waitAndTapById(notesnook.listitem.menu)
|
||||
.wait()
|
||||
.waitAndTapById("icon-lock-unlock")
|
||||
.wait()
|
||||
.isVisibleByText("Lock")
|
||||
.typeTextById(notesnook.ids.dialogs.vault.pwd, "1234")
|
||||
.typeTextById(notesnook.ids.dialogs.vault.pwdAlt, "1234")
|
||||
.waitAndTapByText("Lock")
|
||||
.isVisibleById("note-locked-icon")
|
||||
.run();
|
||||
}
|
||||
|
||||
async function removeFromVault() {
|
||||
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
|
||||
await Tests.sleep(500);
|
||||
await Tests.fromId("icon-lock-unlock").waitAndTap();
|
||||
await Tests.sleep(500);
|
||||
await Tests.fromId(notesnook.ids.dialogs.vault.pwd).element.typeText("1234");
|
||||
await Tests.fromText("Unlock").waitAndTap();
|
||||
await Tests.fromId("note-locked-icon").isNotVisible();
|
||||
await TestBuilder.create()
|
||||
.waitAndTapById(notesnook.listitem.menu)
|
||||
.wait()
|
||||
.waitAndTapById("icon-lock-unlock")
|
||||
.wait()
|
||||
.typeTextById(notesnook.ids.dialogs.vault.pwd, "1234")
|
||||
.waitAndTapByText("Unlock")
|
||||
.isNotVisibleById("note-locked-icon")
|
||||
.run();
|
||||
}
|
||||
|
||||
async function openLockedNote(pwd?: string) {
|
||||
await Tests.fromId(notesnook.ids.note.get(0)).waitAndTap();
|
||||
await Tests.sleep(500);
|
||||
await web()
|
||||
.element(by.web.name("password"))
|
||||
.typeText(pwd || "1234", false);
|
||||
await web().element(by.web.className("unlock-note")).tap();
|
||||
await Tests.sleep(500);
|
||||
await expect(web().element(by.web.className("unlock-note"))).not.toExist();
|
||||
async function openLockedNote(pwd = "1234") {
|
||||
await TestBuilder.create()
|
||||
.waitAndTapById(notesnook.ids.note.get(0))
|
||||
.wait()
|
||||
.addStep(async () => {
|
||||
await web().element(by.web.name("password")).typeText(pwd, false);
|
||||
await web().element(by.web.className("unlock-note")).tap();
|
||||
await Tests.sleep(500);
|
||||
await expect(
|
||||
web().element(by.web.className("unlock-note"))
|
||||
).not.toExist();
|
||||
})
|
||||
.run();
|
||||
}
|
||||
|
||||
async function goToPrivacySecuritySettings() {
|
||||
await Tests.openSideMenu();
|
||||
await Tests.fromId("sidemenu-settings-icon").waitAndTap();
|
||||
await Tests.sleep(300);
|
||||
await Tests.fromText("Settings").waitAndTap();
|
||||
await Tests.fromText("Vault").waitAndTap();
|
||||
await TestBuilder.create()
|
||||
.openSideMenu()
|
||||
.waitAndTapById("sidemenu-settings-icon")
|
||||
.wait()
|
||||
.waitAndTapByText("Settings")
|
||||
.waitAndTapByText("Vault")
|
||||
.run();
|
||||
}
|
||||
|
||||
describe("VAULT", () => {
|
||||
it("Create vault from settings", async () => {
|
||||
await Tests.prepare();
|
||||
await goToPrivacySecuritySettings();
|
||||
await Tests.fromText("Create vault").waitAndTap();
|
||||
await Tests.fromId(notesnook.ids.dialogs.vault.pwd).element.typeText(
|
||||
"1234"
|
||||
);
|
||||
await Tests.fromId(notesnook.ids.dialogs.vault.pwdAlt).element.typeText(
|
||||
"1234"
|
||||
);
|
||||
await Tests.fromText("Create").waitAndTap();
|
||||
await Tests.fromText("Clear vault").isVisible();
|
||||
await TestBuilder.create()
|
||||
.prepare()
|
||||
.addStep(goToPrivacySecuritySettings)
|
||||
.waitAndTapByText("Create vault")
|
||||
.typeTextById(notesnook.ids.dialogs.vault.pwd, "1234")
|
||||
.typeTextById(notesnook.ids.dialogs.vault.pwdAlt, "1234")
|
||||
.waitAndTapByText("Create")
|
||||
.isVisibleByText("Clear vault")
|
||||
.run();
|
||||
});
|
||||
|
||||
it("Change vault password", async () => {
|
||||
await Tests.prepare();
|
||||
await Tests.createNote();
|
||||
await lockNote();
|
||||
await goToPrivacySecuritySettings();
|
||||
await Tests.fromText("Change vault password").waitAndTap();
|
||||
await Tests.fromId(notesnook.ids.dialogs.vault.pwd).element.typeText(
|
||||
"1234"
|
||||
);
|
||||
await Tests.fromId(notesnook.ids.dialogs.vault.changePwd).element.typeText(
|
||||
"2362"
|
||||
);
|
||||
await Tests.fromText("Change").waitAndTap();
|
||||
await device.pressBack();
|
||||
await device.pressBack();
|
||||
await device.pressBack();
|
||||
await Tests.sleep(500);
|
||||
await openLockedNote("2362");
|
||||
it.only("Change vault password", async () => {
|
||||
await TestBuilder.create()
|
||||
.prepare()
|
||||
.createNote()
|
||||
.addStep(lockNote)
|
||||
.addStep(goToPrivacySecuritySettings)
|
||||
.waitAndTapByText("Change vault password")
|
||||
.typeTextById(notesnook.ids.dialogs.vault.pwd, "1234")
|
||||
.typeTextById(notesnook.ids.dialogs.vault.changePwd, "2362")
|
||||
.waitAndTapByText("Change")
|
||||
.pressBack(3)
|
||||
.addStep(async () => await openLockedNote("2362"))
|
||||
.run();
|
||||
});
|
||||
|
||||
it("Delete vault", async () => {
|
||||
await Tests.prepare();
|
||||
await Tests.createNote();
|
||||
await lockNote();
|
||||
await goToPrivacySecuritySettings();
|
||||
await Tests.fromText("Delete vault").waitAndTap();
|
||||
await Tests.fromId(notesnook.ids.dialogs.vault.pwd).element.typeText(
|
||||
"1234"
|
||||
);
|
||||
await Tests.fromText("Delete").waitAndTap();
|
||||
await Tests.sleep(300);
|
||||
await Tests.fromText("Create vault").isVisible();
|
||||
await device.pressBack();
|
||||
await device.pressBack();
|
||||
await device.pressBack();
|
||||
await Tests.fromId(notesnook.listitem.menu).isVisible();
|
||||
await TestBuilder.create()
|
||||
.prepare()
|
||||
.createNote()
|
||||
.addStep(lockNote)
|
||||
.addStep(goToPrivacySecuritySettings)
|
||||
.waitAndTapByText("Delete vault")
|
||||
.typeTextById(notesnook.ids.dialogs.vault.pwd, "1234")
|
||||
.waitAndTapByText("Delete")
|
||||
.isVisibleByText("Create vault")
|
||||
.pressBack(3)
|
||||
.isVisibleById(notesnook.listitem.menu)
|
||||
.run();
|
||||
});
|
||||
|
||||
it("Delete vault with locked notes", async () => {
|
||||
await Tests.prepare();
|
||||
await Tests.createNote();
|
||||
await lockNote();
|
||||
await goToPrivacySecuritySettings();
|
||||
await Tests.fromText("Delete vault").waitAndTap();
|
||||
await Tests.fromId(notesnook.ids.dialogs.vault.pwd).element.typeText(
|
||||
"1234"
|
||||
);
|
||||
await Tests.fromText("Delete notes in this vault").waitAndTap();
|
||||
await Tests.fromText("Delete").waitAndTap();
|
||||
await Tests.sleep(300);
|
||||
await Tests.fromText("Create vault").isVisible();
|
||||
await device.pressBack();
|
||||
await device.pressBack();
|
||||
await device.pressBack();
|
||||
await Tests.fromId(notesnook.listitem.menu).isNotVisible();
|
||||
await TestBuilder.create()
|
||||
.prepare()
|
||||
.createNote()
|
||||
.addStep(lockNote)
|
||||
.addStep(goToPrivacySecuritySettings)
|
||||
.waitAndTapByText("Delete vault")
|
||||
.typeTextById(notesnook.ids.dialogs.vault.pwd, "1234")
|
||||
.waitAndTapByText("Delete notes in this vault")
|
||||
.waitAndTapByText("Delete")
|
||||
.isVisibleByText("Create vault")
|
||||
.pressBack(3)
|
||||
.isNotVisibleById(notesnook.listitem.menu)
|
||||
.run();
|
||||
});
|
||||
|
||||
it("Add a note to vault", async () => {
|
||||
await Tests.prepare();
|
||||
await Tests.createNote();
|
||||
await lockNote();
|
||||
await openLockedNote();
|
||||
await TestBuilder.create()
|
||||
.prepare()
|
||||
.createNote()
|
||||
.addStep(lockNote)
|
||||
.addStep(openLockedNote)
|
||||
.run();
|
||||
});
|
||||
|
||||
it("Remove note from vault", async () => {
|
||||
await Tests.prepare();
|
||||
await Tests.createNote();
|
||||
await lockNote();
|
||||
await removeFromVault();
|
||||
await TestBuilder.create()
|
||||
.prepare()
|
||||
.createNote()
|
||||
.addStep(lockNote)
|
||||
.addStep(removeFromVault)
|
||||
.run();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -124,7 +124,7 @@ android {
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
multiDexEnabled true
|
||||
versionCode 3070
|
||||
versionCode 3071
|
||||
versionName getNpmVersion()
|
||||
testBuildType System.getProperty('testBuildType', 'debug')
|
||||
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
||||
|
||||
@@ -1089,7 +1089,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 2149;
|
||||
CURRENT_PROJECT_VERSION = 2150;
|
||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||
ENABLE_BITCODE = NO;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1163,7 +1163,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 3.3.1;
|
||||
MARKETING_VERSION = 3.3.2;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
@@ -1194,7 +1194,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CURRENT_PROJECT_VERSION = 2149;
|
||||
CURRENT_PROJECT_VERSION = 2150;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1268,7 +1268,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 3.3.1;
|
||||
MARKETING_VERSION = 3.3.2;
|
||||
ONLY_ACTIVE_ARCH = NO;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
@@ -1427,7 +1427,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 2149;
|
||||
CURRENT_PROJECT_VERSION = 2150;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1439,7 +1439,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 3.3.1;
|
||||
MARKETING_VERSION = 3.3.2;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
|
||||
@@ -1470,7 +1470,7 @@
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 2149;
|
||||
CURRENT_PROJECT_VERSION = 2150;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
|
||||
@@ -1483,7 +1483,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 3.3.1;
|
||||
MARKETING_VERSION = 3.3.2;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
@@ -1513,7 +1513,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = "Make Note/Make Note.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 2149;
|
||||
CURRENT_PROJECT_VERSION = 2150;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1594,7 +1594,7 @@
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift$(inherited)";
|
||||
MARKETING_VERSION = 3.3.1;
|
||||
MARKETING_VERSION = 3.3.2;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
|
||||
@@ -1625,7 +1625,7 @@
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 2149;
|
||||
CURRENT_PROJECT_VERSION = 2150;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
|
||||
@@ -1707,7 +1707,7 @@
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift$(inherited)";
|
||||
MARKETING_VERSION = 3.3.1;
|
||||
MARKETING_VERSION = 3.3.2;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@notesnook/mobile",
|
||||
"version": "3.3.1",
|
||||
"version": "3.3.2",
|
||||
"private": true,
|
||||
"license": "GPL-3.0-or-later",
|
||||
"workspaces": [
|
||||
|
||||
@@ -87,6 +87,8 @@ export async function scheduleFullBackups() {
|
||||
}
|
||||
|
||||
export async function shouldAddAutoBackupsDisabledNotice() {
|
||||
if (isIgnored("autoBackupsOff")) return false;
|
||||
|
||||
const user = await db.user.getUser();
|
||||
if (!user) return false;
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ import { updateStatus } from "../hooks/use-status";
|
||||
import {
|
||||
Gateway,
|
||||
WEB_EXTENSION_CHANNEL_EVENTS
|
||||
} from "@notesnook/web-clipper/dist/common/bridge";
|
||||
} from "@notesnook/web-clipper/common/bridge.js";
|
||||
import { Extension } from "../components/icons";
|
||||
|
||||
export class WebExtensionRelay {
|
||||
|
||||
@@ -23,7 +23,7 @@ import {
|
||||
NotebookReference,
|
||||
Server,
|
||||
Clip
|
||||
} from "@notesnook/web-clipper/dist/common/bridge";
|
||||
} from "@notesnook/web-clipper/common/bridge.js";
|
||||
import { store as appstore } from "../stores/app-store";
|
||||
import { h } from "./html";
|
||||
import { sanitizeFilename } from "@notesnook/common";
|
||||
|
||||
1
docs/help/contents/self-hosting/README.md
Normal file
1
docs/help/contents/self-hosting/README.md
Normal file
@@ -0,0 +1 @@
|
||||
# Self hosting
|
||||
295
docs/help/contents/self-hosting/getting-started.md
Normal file
295
docs/help/contents/self-hosting/getting-started.md
Normal file
@@ -0,0 +1,295 @@
|
||||
# Getting started
|
||||
|
||||
## Minimum server requirements
|
||||
|
||||
- **Operating system**: Linux
|
||||
- **RAM**: 1 GB
|
||||
- **Storage**: 20 GB
|
||||
- **CPU**: any x64 or ARM CPU will do
|
||||
|
||||
## Prerequisites
|
||||
|
||||
1. Docker
|
||||
2. Docker Compose
|
||||
3. wget
|
||||
4. curl
|
||||
5. (Optional) Caddy or Nginx for reverse proxying
|
||||
|
||||
You **must** have a working Linux server with SSH access.
|
||||
|
||||
> warn Disclaimer
|
||||
>
|
||||
> How you secure or setup your server is up to you, and this guide will NOT provide any recommendations outside what is directly relevant to Notesnook.
|
||||
>
|
||||
> Self hosting means managing everything on your own so **we will not be responsible if you lose any data** during setup, upgrade, or any other step. It is recommended that you create regular backups in both the app and on the server to avoid this.
|
||||
|
||||
## Installation
|
||||
|
||||
1. Create a directory where configuration files can be placed:
|
||||
```sh
|
||||
mkdir notesnook-sync-server
|
||||
```
|
||||
2. Navigate into the newly created directory:
|
||||
```sh
|
||||
cd notesnook-sync-server
|
||||
```
|
||||
3. Download the `docker-compose.yml` file:
|
||||
```sh
|
||||
wget https://raw.githubusercontent.com/streetwriters/notesnook-sync-server/master/docker-compose.yml
|
||||
```
|
||||
4. Download the `.env` file:
|
||||
```sh
|
||||
wget https://raw.githubusercontent.com/streetwriters/notesnook-sync-server/master/.env
|
||||
```
|
||||
|
||||
You should end up with the following files in the `notesnook-sync-server` directory:
|
||||
|
||||
- docker-compose.yml
|
||||
- .env
|
||||
|
||||
## Configuration
|
||||
|
||||
All configuration is done via the `.env` file. You'll need to change very few settings to get the self-hosted containers up and running.
|
||||
|
||||
### `INSTANCE_NAME` (required)
|
||||
|
||||
This is used by the client apps to identify which instance you are using. You can just leave it the default value if you like, or set it to a personalized value like `john-doe-notesnook-server`.
|
||||
|
||||
### `NOTESNOOK_API_SECRET` (required)
|
||||
|
||||
The `NOTESNOOK_API_SECRET` is used for authenticating & validating the access tokens. This must be a randomly generated value otherwise anyone will be able to make authenticated requests to your server.
|
||||
|
||||
You can use OpenSSL to generate a secure random secret like this:
|
||||
|
||||
```sh
|
||||
openssl rand -hex 32
|
||||
```
|
||||
|
||||
### SMTP settings (required)
|
||||
|
||||
SMTP settings are required to enable sending 2FA and account reset emails. You can either use your email provider's SMTP settings (most email providers allow sending emails via SMTP) or you can create a free account on [SendGrid](http://sendgrid.com/) or [Brevo/SendInBlue](https://www.brevo.com). Both of these providers allow sending emails via SMTP in their free tiers.
|
||||
|
||||
| Variable | Description | Required? | Example |
|
||||
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | --------- | -------------- |
|
||||
| `SMTP_USERNAME` | Username for the SMTP connection. Usually it is the email address but check your email provider's documentation to see what you should put here. | yes |
|
||||
| `SMTP_PASSWORD` | Password for the SMTP connection. Check your email provider's documentation to see what you should put here. | yes |
|
||||
| `SMTP_HOST` | Host on which the the SMTP connection is accessible. | yes | smtp.gmail.com |
|
||||
| `SMTP_PORT` | Port on which the the SMTP connection is accessible. | yes | 465 |
|
||||
|
||||
### Public URL settings (required)
|
||||
|
||||
Public URLs are how the servers can generate valid publicly accessible URLs for different things like email confirmation, password reset links etc. These URLs must be accessible from _outside_ of where you are hosting your servers (e.g. by using a reverse proxy like Nginx).
|
||||
|
||||
| Variable | Description | Required? | Example |
|
||||
| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | --------- | ------------------------- |
|
||||
| `NOTESNOOK_APP_PUBLIC_URL` | If you are self hosting the web app, you should put its URL here. Otherwise leave it as is. | yes | https://app.notesnook.com |
|
||||
| `AUTH_SERVER_PUBLIC_URL` | Publicly accessible URL for the auth server. You usually expose this URL via a reverse proxy like Caddy/Nginx. | yes | https://auth.example.com |
|
||||
| `MONOGRAPH_PUBLIC_URL` | Publicly accessible URL for the monographs. You usually expose this URL via a reverse proxy like Caddy/Nginx. | yes | https://monogr.ph |
|
||||
| `ATTACHMENTS_SERVER_PUBLIC_URL` | Publicly accessible URL for the attachments (aka S3) server. You usually expose this URL via a reverse proxy like Caddy/Nginx. | yes | https://s3.example.com |
|
||||
|
||||
### `DISABLE_SIGNUPS` (required)
|
||||
|
||||
Set this to `true` to disable new accounts from being created on your server.
|
||||
|
||||
### Minio settings (optional)
|
||||
|
||||
| Variable | Description | Default |
|
||||
| --------------------- | ------------------------------------------ | ---------- |
|
||||
| `MINIO_ROOT_USER` | Custom username for the root Minio account | minioadmin |
|
||||
| `MINIO_ROOT_PASSWORD` | Custom password for the root Minio account | minioadmin |
|
||||
|
||||
You should change these values to something random & secure just to be safe. The Minio internal API & dashboard ports are not exposed for external use so you are probably safe with the default values but don't take my word for it. Change the password. You won't regret it.
|
||||
|
||||
### `NOTESNOOK_CORS_ORIGINS` (optional)
|
||||
|
||||
CORS is used to prevent unknown and arbitrary origins from making requests to your server. This is useful, for example, if you want to allow requests only from `https://app.notesnook.com` and reject everything else.
|
||||
|
||||
By default, all origins are allowed which means anyone from any domain can make requests to your server. You probably don't want this if you are using self hosted clients.
|
||||
|
||||
For example, to restrict traffic only to `yourdomain.com`, you should set this to:
|
||||
|
||||
```
|
||||
NOTESNOOK_CORS_ORIGINS=https://yourdomain.com
|
||||
```
|
||||
|
||||
If you want to allow multiple domains, just separate them using commas:
|
||||
|
||||
```
|
||||
NOTESNOOK_CORS_ORIGINS=https://yourdomain.com,https://yourotherdomain.com
|
||||
```
|
||||
|
||||
### Twilio settings (optional)
|
||||
|
||||
Twilio is used for SMS based 2FA. If you don't plan on using 2FA via SMS then leave these settings empty.
|
||||
|
||||
| Variable | Description | Example |
|
||||
| -------------------- | ------------------------------------------------------------------------------------------------------------------------ | ---------------------------------- |
|
||||
| `TWILIO_ACCOUNT_SID` | [Learn more here](https://help.twilio.com/articles/14726256820123-What-is-a-Twilio-Account-SID-and-where-can-I-find-it-) |
|
||||
| `TWILIO_AUTH_TOKEN` | [Learn more here](https://help.twilio.com/articles/223136027-Auth-Tokens-and-How-to-Change-Them) |
|
||||
| `TWILIO_SERVICE_SID` | The unique string that we created to identify the Service resource. | VAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa |
|
||||
|
||||
## Starting the server
|
||||
|
||||
Once you are done configuring your server, it's time to run it:
|
||||
|
||||
```sh
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
To make sure everything is working well, run:
|
||||
|
||||
```sh
|
||||
docker ps
|
||||
```
|
||||
|
||||
You should see something like this:
|
||||
|
||||
```
|
||||
3c39da9194db streetwriters/sse:latest "./Streetwriters.Mes…" 38 minutes ago Up 38 minutes (healthy) 0.0.0.0:7264->7264/tcp, :::7264->7264/tcp notesnook-sse-server-1
|
||||
19c4a6536578 streetwriters/monograph:latest "docker-entrypoint.s…" 38 minutes ago Up 38 minutes (healthy) 0.0.0.0:6264->3000/tcp, [::]:6264->3000/tcp notesnook-monograph-server-1
|
||||
7b9db61b5d0d streetwriters/notesnook-sync:latest "./Notesnook.API" 38 minutes ago Up 38 minutes (healthy) 0.0.0.0:5264->5264/tcp, :::5264->5264/tcp notesnook-notesnook-server-1
|
||||
6491b172817e streetwriters/identity:latest "./Streetwriters.Ide…" 38 minutes ago Up 38 minutes (healthy) 0.0.0.0:8264->8264/tcp, :::8264->8264/tcp notesnook-identity-server-1
|
||||
bfb71f21e57b minio/minio:RELEASE.2024-07-29T22-14-52Z "/usr/bin/docker-ent…" 38 minutes ago Up 38 minutes (healthy) 0.0.0.0:9000->9000/tcp, :::9000->9000/tcp notesnook-notesnook-s3-1
|
||||
d27f6207fb93 mongo:7.0.12 "docker-entrypoint.s…" 38 minutes ago Up 38 minutes (healthy) 27017/tcp notesnook-notesnook-db-1
|
||||
2bde52e0102d willfarrell/autoheal:latest "/docker-entrypoint …" 38 minutes ago Up 38 minutes (healthy) notesnook-autoheal-1
|
||||
```
|
||||
|
||||
In total, you should have 7 containers up and running. If there are less than 7 that means something failed. If you see `Unhealthy` anywhere, that means something went wrong. Refer to the troubleshooting guide to see how you can debug this.
|
||||
|
||||
## Exposing the servers to the Internet
|
||||
|
||||
Running the docker containers on device is all well and good but if you want to connect your other devices, sync your notes, you need to expose the servers over the Internet. Even if you only require local access, it is recommended that you use something like Tailscale or Cloudflare Tunnels to securely & reliably expose the Notesnook servers.
|
||||
|
||||
> error HTTPS is required
|
||||
>
|
||||
> You won't be able to sync or login without HTTPS. This is **not** mandated by Notesnook but by the vendors running the client apps (i.e. all major browsers diallow HTTP connections).
|
||||
|
||||
> info
|
||||
>
|
||||
> These instructions assume that you did not change the ports for the servers inside the `docker-compose.yml` file. If you did change the default ports, you'll have to use those instead.
|
||||
|
||||
### Using Caddy (recommended)
|
||||
|
||||
Caddy is one of the simplest ways to reverse proxy traffic from the Internet to your servers.
|
||||
|
||||
> info
|
||||
>
|
||||
> If you already have a `Caddyfile`, just use that.
|
||||
|
||||
1. Create or edit your `Caddyfile` (you can do this literally anywhere). For default installations, you'll find the `Caddyfile` in `/etc/caddy/Caddyfile`.
|
||||
2. Paste the following configuration into the `Caddyfile`
|
||||
|
||||
```
|
||||
sync.example.com {
|
||||
reverse_proxy :5264
|
||||
}
|
||||
|
||||
auth.example.com {
|
||||
reverse_proxy :8264
|
||||
}
|
||||
|
||||
sse.example.com {
|
||||
reverse_proxy :7264
|
||||
}
|
||||
|
||||
monograph.example.com {
|
||||
reverse_proxy :6264
|
||||
}
|
||||
|
||||
s3.example.com {
|
||||
reverse_proxy :9000
|
||||
}
|
||||
```
|
||||
|
||||
3. Change the domain names as appropriate.
|
||||
4. Restart Caddy (run `systemctl restart caddy` if you are running it as a daemon)
|
||||
5. And done! You should now have a working SSL connection to all your servers.
|
||||
|
||||
### Using Nginx
|
||||
|
||||
Configuring Nginx is a bit more complicated than Caddy. If you are a beginner, you should probably stick to using Caddy. This guide assumes you already know how Nginx works and how to configure it.
|
||||
|
||||
1. Create an Nginx configuration file in `/etc/nginx/sites-available` and paste the following into it:
|
||||
|
||||
```nginx
|
||||
# Sync server
|
||||
server {
|
||||
server_name sync.example.com;
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:5264;
|
||||
include proxy_params;
|
||||
|
||||
proxy_redirect off; # required
|
||||
proxy_http_version 1.1; # required
|
||||
proxy_set_header Upgrade $http_upgrade; # required
|
||||
proxy_set_header Connection "upgrade"; # required
|
||||
}
|
||||
}
|
||||
|
||||
# SSE server
|
||||
server {
|
||||
server_name sse.example.com;
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:7264;
|
||||
include proxy_params;
|
||||
|
||||
proxy_set_header Connection ''; # required
|
||||
proxy_http_version 1.1; # required
|
||||
chunked_transfer_encoding off; # required
|
||||
proxy_buffering off; # required
|
||||
proxy_cache off; # required
|
||||
}
|
||||
}
|
||||
|
||||
# Auth server
|
||||
server {
|
||||
server_name auth.example.com;
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:8264;
|
||||
include proxy_params;
|
||||
}
|
||||
}
|
||||
|
||||
# Monograph server
|
||||
server {
|
||||
server_name monograph.example.com;
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:6264;
|
||||
include proxy_params;
|
||||
}
|
||||
}
|
||||
|
||||
# S3 server
|
||||
server {
|
||||
server_name s3.example.com;
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:9000;
|
||||
include proxy_params;
|
||||
client_max_body_size 1024M; # required
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2. Change the `server_name`s as appropriate.
|
||||
3. Run `nginx -t` and make sure there are no errors
|
||||
4. Restart nginx (`systemctl restart nginx`)
|
||||
5. And done! You should have a working HTTP connection to all your servers.
|
||||
6. **To enable SSL, [use CertBot](https://certbot.eff.org/instructions?ws=nginx&os=snap).**
|
||||
|
||||
> info Cloudflare DNS users
|
||||
>
|
||||
> If you are using Cloudflare for DNS, make sure the DNS settings for these domains are set to DNS-only instead of Proxied. While the Proxied mode might work, it is not tested so use at your own risk.
|
||||
|
||||
## Testing the connection
|
||||
|
||||
You installed, configured, and set up everything but how can you be sure your devices can access your servers? That's where `curl` comes in.
|
||||
|
||||
All the above services provide a healthcheck endpoint to allow checking their status. You can run a healthcheck like this:
|
||||
|
||||
```sh
|
||||
curl https://sync.example.com/health
|
||||
```
|
||||
|
||||
You should see `Healthy` printed on the screen.
|
||||
|
||||
## Connecting the client apps
|
||||
51
docs/help/contents/self-hosting/overview.md
Normal file
51
docs/help/contents/self-hosting/overview.md
Normal file
@@ -0,0 +1,51 @@
|
||||
# Overview
|
||||
|
||||
In addition to being 100% open source, Notesnook offers a complete setup for self hosting. This version is provided as-is without any kind of guarantees or dedicated support. We'll do our best to keep self hosting functional but if you face any issues specific to your hosting setup, you are on your own.
|
||||
|
||||
Self hosting is a niche art and it is expected that you have all the basic sysadmin knowledge required to run & secure your server. This guide will try to provide solutions for most common scenarios and setups but anything more complex is out of scope.
|
||||
|
||||
If you already have an account on Notesnook SaaS and want to switch to a self hosted setup, check out the following guide.
|
||||
|
||||
## Infrastructure
|
||||
|
||||
The self hosted setup consists of 5 services and 1 database:
|
||||
|
||||
1. `notesnook-db`: MonogDB for storing your data
|
||||
2. `notesnook-s3`: Minio for storing your attachments
|
||||
3. `identity-server`: For auth & login
|
||||
4. `sync-server`: For syncing your notes
|
||||
5. `sse-server`: For sending events & notifications to the client apps.
|
||||
6. `monograph-server`: For serving published monographs
|
||||
|
||||
## Differences between self-hosted and SaaS
|
||||
|
||||
The following things are not included in the self-hosted setup:
|
||||
|
||||
1. Billing & subscriptions system
|
||||
2. Issues & feedback system
|
||||
3. Desktop, mobile & web clients
|
||||
4. User administration sytem
|
||||
|
||||
Everything else is exactly the same.
|
||||
|
||||
## Intended audience
|
||||
|
||||
This guide is targeted towards people who want to self host Notesnook for personal use. While you can create multiple accounts on your self-hosted instance, we provide no support or guarantees on if/how it'll work.
|
||||
|
||||
## FAQs
|
||||
|
||||
### Do I need to purchase a subscription in order to self-host?
|
||||
|
||||
No. Self-hosting is completely independent and does not require any kind of connection or authentication with the official Notesnook instance.
|
||||
|
||||
### Will Notesnook (the company) know I am self-hosting?
|
||||
|
||||
No. There is no telemetry on either the server or the clients that'd let us know whether someone is self-hosting or not.
|
||||
|
||||
### Do I need to use the official apps if I am self-hosting?
|
||||
|
||||
No. You can compile all the apps from source and run them that way if you like.
|
||||
|
||||
### Can I get help from you if I face any issue during self-hosting?
|
||||
|
||||
No. This guide is provided with the assumption that _it works_. Any edge cases or specific setups are not handled or expected.
|
||||
@@ -80,6 +80,10 @@ navigation:
|
||||
- path: custom-themes/create-a-theme-with-theme-builder.md
|
||||
- path: custom-themes/install-a-theme-from-file.md
|
||||
- path: custom-themes/publish-a-theme.md
|
||||
- path: self-hosting
|
||||
children:
|
||||
- path: self-hosting/overview.md
|
||||
- path: self-hosting/getting-started.md
|
||||
- path: faqs
|
||||
children:
|
||||
- path: faqs/what-are-merge-conflicts.md
|
||||
|
||||
@@ -2,7 +2,31 @@
|
||||
"name": "@notesnook/web-clipper",
|
||||
"version": "0.3.4",
|
||||
"private": true,
|
||||
"main": "./dist/index.js",
|
||||
"main": "./dist/cjs/index.js",
|
||||
"module": "./dist/esm/index.js",
|
||||
"types": "./dist/types/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"require": {
|
||||
"types": "./dist/types/index.d.ts",
|
||||
"default": "./dist/cjs/index.js"
|
||||
},
|
||||
"import": {
|
||||
"types": "./dist/types/index.d.ts",
|
||||
"default": "./dist/esm/index.js"
|
||||
}
|
||||
},
|
||||
"./*": {
|
||||
"require": {
|
||||
"types": "./dist/types/*",
|
||||
"default": "./dist/cjs/*"
|
||||
},
|
||||
"import": {
|
||||
"types": "./dist/types/*",
|
||||
"default": "./dist/esm/*"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@emotion/react": "11.11.1",
|
||||
"@mdi/js": "7.4.47",
|
||||
@@ -28,7 +52,7 @@
|
||||
"build:chrome": "cross-env MANIFEST_VERSION=3 node build-utils/build.js",
|
||||
"dev:chrome": "cross-env MANIFEST_VERSION=3 node build-utils/dev.js",
|
||||
"dev:firefox": "cross-env MANIFEST_VERSION=2 node build-utils/dev.js",
|
||||
"build": "tsc"
|
||||
"build": "node ../../scripts/build.mjs"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
@@ -94,4 +118,4 @@
|
||||
},
|
||||
"homepage": "https://notesnook.com/",
|
||||
"repository": "https://github.com/streetwriters/notesnook"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"$schema": "node_modules/lerna/schemas/lerna-schema.json",
|
||||
"useNx": true,
|
||||
"version": "independent",
|
||||
"packages": ["packages/*", "apps/*", "extensions/*", "servers/*"]
|
||||
}
|
||||
76
nx.json
76
nx.json
@@ -1,76 +0,0 @@
|
||||
{
|
||||
"targetDefaults": {
|
||||
"build": {
|
||||
"dependsOn": ["^build"],
|
||||
"outputs": [
|
||||
"{projectRoot}/build",
|
||||
"{projectRoot}/dist",
|
||||
"{projectRoot}/languages",
|
||||
"{projectRoot}/locales",
|
||||
"{projectRoot}/src/extensions/code-block/languages"
|
||||
],
|
||||
"cache": true
|
||||
},
|
||||
"release": {
|
||||
"dependsOn": ["^build"],
|
||||
"outputs": ["{projectRoot}/build"]
|
||||
},
|
||||
"build:test": {
|
||||
"dependsOn": ["^build"],
|
||||
"outputs": ["{projectRoot}/build"],
|
||||
"cache": true
|
||||
},
|
||||
"build:beta": {
|
||||
"dependsOn": ["^build"],
|
||||
"outputs": ["{projectRoot}/build"]
|
||||
},
|
||||
"build:desktop": {
|
||||
"dependsOn": ["^build"],
|
||||
"outputs": ["{projectRoot}/build"],
|
||||
"cache": true
|
||||
},
|
||||
"start": {
|
||||
"dependsOn": ["^build"]
|
||||
},
|
||||
"start:desktop": {
|
||||
"dependsOn": ["^build"]
|
||||
},
|
||||
"test:e2e": {
|
||||
"dependsOn": ["^build"]
|
||||
},
|
||||
"test": {
|
||||
"dependsOn": ["^build"]
|
||||
},
|
||||
"run-android": {
|
||||
"dependsOn": ["^build"]
|
||||
},
|
||||
"run-ios": {
|
||||
"dependsOn": ["^build"]
|
||||
},
|
||||
"build-ios": {
|
||||
"dependsOn": ["^build"]
|
||||
},
|
||||
"build-android": {
|
||||
"dependsOn": ["^build"]
|
||||
},
|
||||
"release-android": {
|
||||
"dependsOn": ["^build"]
|
||||
},
|
||||
"release-android-bundle": {
|
||||
"dependsOn": ["^build"]
|
||||
},
|
||||
"release-ios": {
|
||||
"dependsOn": ["^build"]
|
||||
},
|
||||
"repack": {
|
||||
"dependsOn": ["^build"]
|
||||
}
|
||||
},
|
||||
"namedInputs": {
|
||||
"default": ["{projectRoot}/**/*", "sharedGlobals"],
|
||||
"sharedGlobals": [],
|
||||
"production": ["default"]
|
||||
},
|
||||
"parallel": 4,
|
||||
"nxCloudAccessToken": "ZWM3ODgxNDgtNGUzZC00MjQ0LWE3MzMtZDdhMzE3ZGY2MWFlfHJlYWQ="
|
||||
}
|
||||
4682
package-lock.json
generated
4682
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
117
package.json
117
package.json
@@ -5,46 +5,49 @@
|
||||
"prepare": "husky install && npm run bootstrap",
|
||||
"bootstrap": "node scripts/bootstrap.mjs",
|
||||
"clean": "node scripts/clean.mjs",
|
||||
"build": "nx run-many --target=build --all --exclude=@notesnook/mobile,@notesnook/web",
|
||||
"build:web": "nx build @notesnook/web",
|
||||
"build:vericrypt": "nx build @notesnook/vericrypt",
|
||||
"build:test:web": "nx build:test @notesnook/web",
|
||||
"build:beta:web": "nx build:beta @notesnook/web",
|
||||
"start:web": "nx start @notesnook/web",
|
||||
"start:theme-builder": "nx start @notesnook/theme-builder",
|
||||
"build:theme-builder": "nx build @notesnook/theme-builder",
|
||||
"start:vericrypt": "nx start @notesnook/vericrypt",
|
||||
"start:desktop": "nx start @notesnook/desktop",
|
||||
"test:web": "nx test @notesnook/web",
|
||||
"test:core": "nx test @notesnook/core",
|
||||
"start:android": "nx run-android @notesnook/mobile",
|
||||
"start:ios": "nx run-ios @notesnook/mobile",
|
||||
"prepare:ios": "nx install-pods @notesnook/mobile",
|
||||
"build:ios": "nx build-ios @notesnook/mobile",
|
||||
"build:android": "nx build-android @notesnook/mobile",
|
||||
"build:android:debug": "nx build-android-debug @notesnook/mobile",
|
||||
"release:android": "nx release-android @notesnook/mobile",
|
||||
"release:android:bundle": "nx release-android-bundle @notesnook/mobile",
|
||||
"test:ios": "nx e2e-ios @notesnook/mobile",
|
||||
"test:android": "nx e2e-android @notesnook/mobile",
|
||||
"test:android:debug": "nx e2e-android-debug @notesnook/mobile",
|
||||
"start:metro": "nx start @notesnook/mobile",
|
||||
"start:repack": "nx repack @notesnook/mobile",
|
||||
"start:editor-mobile": "nx start @notesnook/editor-mobile",
|
||||
"start:editor": "nx start @notesnook/editor",
|
||||
"start:server:themes": "nx start @notesnook/themes-server",
|
||||
"build:intl": "nx build @notesnook/intl",
|
||||
"start:intl": "nx start @notesnook/intl",
|
||||
"build": "npm run tx -- build --all --exclude=mobile,web,monograph,theme-builder,vericrypt",
|
||||
"build:web": "npm run tx web:build",
|
||||
"build:vericrypt": "npm run tx vericrypt:build",
|
||||
"build:test:web": "npm run tx web:build:test",
|
||||
"build:beta:web": "npm run tx web:build:beta",
|
||||
"start:web": "npm run tx web:start",
|
||||
"start:theme-builder": "npm run tx theme-builder:start",
|
||||
"build:theme-builder": "npm run tx theme-builder:build",
|
||||
"start:vericrypt": "npm run tx vericrypt:start",
|
||||
"start:desktop": "npm run tx desktop:start",
|
||||
"test:web": "npm run tx web:test",
|
||||
"test:core": "npm run tx core:test",
|
||||
"start:android": "npm run tx mobile:run-android",
|
||||
"start:ios": "npm run tx mobile:run-ios",
|
||||
"prepare:ios": "npm run tx mobile:install-pods",
|
||||
"build:ios": "npm run tx mobile:build-ios",
|
||||
"build:android": "npm run tx mobile:build-android",
|
||||
"build:android:debug": "npm run tx mobile:build-android-debug",
|
||||
"release:android": "npm run tx mobile:release-android",
|
||||
"release:android:bundle": "npm run tx mobile:release-android-bundle",
|
||||
"test:ios": "npm run tx mobile:e2e-ios",
|
||||
"test:android": "npm run tx mobile:e2e-android",
|
||||
"test:android:debug": "npm run tx mobile:e2e-android-debug",
|
||||
"start:metro": "npm run tx mobile:start",
|
||||
"start:repack": "npm run tx mobile:repack",
|
||||
"start:editor-mobile": "npm run tx editor-mobile:start",
|
||||
"start:editor": "npm run tx editor:start",
|
||||
"start:server:themes": "npm run tx themes-server:start",
|
||||
"build:intl": "npm run tx intl:build",
|
||||
"start:intl": "npm run tx intl:start",
|
||||
"prettier": "npx prettier . --write",
|
||||
"lint": "npx eslint ./apps/ ./packages/",
|
||||
"linc": "npx eslint ./apps/ ./packages/ --cache"
|
||||
"linc": "npx eslint ./apps/ ./packages/ --cache",
|
||||
"tx": "node scripts/execute.mjs"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^17.1.1",
|
||||
"@types/node": "^18.7.13",
|
||||
"@typescript-eslint/eslint-plugin": "^5.59.9",
|
||||
"@typescript-eslint/parser": "^5.59.9",
|
||||
"@typescript/native-preview": "^7.0.0-dev.20251006.1",
|
||||
"cross-env": "^7.0.3",
|
||||
"dotenv": "^17.2.3",
|
||||
"eslint": "^8.42.0",
|
||||
"eslint-config-react-app": "^7.0.1",
|
||||
"eslint-config-react-hooks": "^1.0.0",
|
||||
@@ -58,13 +61,13 @@
|
||||
"husky": "^8.0.1",
|
||||
"prettier": "^2.8.8",
|
||||
"serve": "^14.0.1",
|
||||
"tsdown": "^0.15.6",
|
||||
"tsup": "^8.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"eslint-plugin-header": "^3.1.1",
|
||||
"fdir": "^6.0.2",
|
||||
"fdir": "^6.5.0",
|
||||
"listr2": "^8.2.3",
|
||||
"nx": "^21.6.3",
|
||||
"patch-package": "^7.0.0",
|
||||
"rxjs": "^7.8.1",
|
||||
"typescript": "^5.6.3",
|
||||
@@ -72,26 +75,42 @@
|
||||
},
|
||||
"license": "GPL-3.0-or-later",
|
||||
"volta": {
|
||||
"node": "20.12.0"
|
||||
"node": "22.20.0"
|
||||
},
|
||||
"taskRunner": {
|
||||
"projects": [
|
||||
"packages/**",
|
||||
"apps/**",
|
||||
"extensions/**",
|
||||
"servers/**"
|
||||
"packages/*",
|
||||
"apps/*",
|
||||
"extensions/*",
|
||||
"servers/*"
|
||||
],
|
||||
"taskDependencies": {
|
||||
"build": [
|
||||
"build"
|
||||
],
|
||||
"release": [
|
||||
"build"
|
||||
],
|
||||
"build:desktop": [
|
||||
"build"
|
||||
]
|
||||
}
|
||||
"tasks": [
|
||||
{
|
||||
"dependencies": [
|
||||
"build"
|
||||
],
|
||||
"commands": [
|
||||
"start",
|
||||
"build",
|
||||
"release",
|
||||
"build:test",
|
||||
"build:beta",
|
||||
"build:desktop",
|
||||
"start",
|
||||
"start:desktop",
|
||||
"test:e2e",
|
||||
"test",
|
||||
"run-android",
|
||||
"run-ios",
|
||||
"build-ios",
|
||||
"build-android",
|
||||
"release-android",
|
||||
"release-android-bundle",
|
||||
"release-ios",
|
||||
"repack"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
|
||||
}
|
||||
|
||||
2755
packages/clipper/package-lock.json
generated
2755
packages/clipper/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -11,9 +11,8 @@
|
||||
"main": "dist/index.js",
|
||||
"devDependencies": {
|
||||
"@playwright/test": "1.48.2",
|
||||
"slugify": "1.6.6",
|
||||
"webpack": "5.88.2",
|
||||
"webpack-cli": "5.1.4"
|
||||
"@rsbuild/core": "^1.5.13",
|
||||
"slugify": "1.6.6"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
@@ -23,7 +22,7 @@
|
||||
"url": "git+https://github.com/streetwriters/notesnook.git"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc && yarn webpack -c webpack.config.js",
|
||||
"build": "tsc && npx rsbuild build",
|
||||
"prepublishOnly": "npm run build",
|
||||
"test": "playwright test",
|
||||
"postinstall": "patch-package",
|
||||
@@ -38,4 +37,4 @@
|
||||
"hyperapp": "^2.0.22",
|
||||
"specificity": "^0.4.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,15 +17,27 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
const path = require("path");
|
||||
module.exports = {
|
||||
entry: ["./dist/index.global.js"],
|
||||
import { defineConfig } from "@rsbuild/core";
|
||||
import path from "path";
|
||||
|
||||
export default defineConfig({
|
||||
source: {
|
||||
entry: {
|
||||
global: "./dist/index.global.js"
|
||||
}
|
||||
},
|
||||
mode: "production",
|
||||
output: {
|
||||
filename: "clipper.bundle.js",
|
||||
path: path.resolve(
|
||||
__dirname,
|
||||
"../../apps/mobile/native/ios/extension.bundle"
|
||||
)
|
||||
target: "node",
|
||||
cleanDistPath: false,
|
||||
filename: {
|
||||
js: "clipper.bundle.js"
|
||||
},
|
||||
distPath: {
|
||||
root: path.resolve(
|
||||
__dirname,
|
||||
"../../apps/mobile/native/ios/extension.bundle"
|
||||
)
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -222,7 +222,7 @@ export const tiptapKeys = {
|
||||
type: "tiptap"
|
||||
},
|
||||
insertParagraph: {
|
||||
keys: "Mod-Shift-0",
|
||||
keys: "Mod-Alt-0",
|
||||
description: "Insert paragraph",
|
||||
category: "Editor",
|
||||
type: "tiptap"
|
||||
|
||||
@@ -68,20 +68,23 @@ test("editing the same note should create multiple history sessions", () =>
|
||||
|
||||
test("restoring an old session should replace note's content", () =>
|
||||
noteTest({ ...TEST_NOTE, sessionId: Date.now() }).then(async ({ db, id }) => {
|
||||
await delay(1000);
|
||||
let editedContent = {
|
||||
data: TEST_NOTE.content.data + "<p>Some new content</p>",
|
||||
type: "tiptap"
|
||||
};
|
||||
|
||||
const sessionId = `${Date.now() + 10000}`;
|
||||
await db.notes.add({
|
||||
id: id,
|
||||
content: editedContent,
|
||||
sessionId: Date.now() + 10000
|
||||
sessionId
|
||||
});
|
||||
|
||||
const [, firstVersion] = await db.noteHistory
|
||||
.get(id)
|
||||
.items(undefined, { sortBy: "dateModified", sortDirection: "desc" });
|
||||
expect(firstVersion.id).not.toBe(`${id}_${sessionId}`);
|
||||
await db.noteHistory.restore(firstVersion.id);
|
||||
|
||||
const contentId = (await db.notes.note(id)).contentId;
|
||||
@@ -138,15 +141,17 @@ test("return empty array if no history available", () =>
|
||||
|
||||
test("auto clear sessions if they exceed the limit", () =>
|
||||
noteTest({ ...TEST_NOTE, sessionId: Date.now() }).then(async ({ db, id }) => {
|
||||
await delay(1000);
|
||||
let editedContent = {
|
||||
data: TEST_NOTE.content.data + "<p>Some new content</p>",
|
||||
type: "tiptap"
|
||||
};
|
||||
|
||||
const sessionId = `${Date.now() + 10000}`;
|
||||
await db.notes.add({
|
||||
id: id,
|
||||
content: editedContent,
|
||||
sessionId: `${Date.now() + 10000}`
|
||||
sessionId
|
||||
});
|
||||
|
||||
expect(await db.noteHistory.get(id).count()).toBe(2);
|
||||
@@ -157,6 +162,7 @@ test("auto clear sessions if they exceed the limit", () =>
|
||||
expect(sessions).toHaveLength(1);
|
||||
|
||||
const content = await db.noteHistory.content(sessions[0].id);
|
||||
expect(sessions[0].id).toBe(`${id}_${sessionId}`);
|
||||
expect(content.data).toBe(editedContent.data);
|
||||
}));
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsup-node src/index.ts",
|
||||
"build": "tsdown src/index.ts",
|
||||
"prepublishOnly": "npm run build",
|
||||
"watch": "npm run build -- --watch"
|
||||
},
|
||||
|
||||
711
packages/editor/package-lock.json
generated
711
packages/editor/package-lock.json
generated
@@ -66,6 +66,7 @@
|
||||
"devDependencies": {
|
||||
"@emotion/react": "11.11.1",
|
||||
"@mdi/js": "7.4.47",
|
||||
"@rslib/core": "^0.15.0",
|
||||
"@theme-ui/components": "0.16.1",
|
||||
"@theme-ui/core": "0.16.1",
|
||||
"@types/katex": "0.16.7",
|
||||
@@ -77,6 +78,7 @@
|
||||
"happy-dom": "16.0.1",
|
||||
"isomorphic-fetch": "^3.0.0",
|
||||
"nodemon": "^3.1.7",
|
||||
"prismjs": "^1.30.0",
|
||||
"prosemirror-test-builder": "^1.1.1",
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.3.1",
|
||||
@@ -260,6 +262,180 @@
|
||||
"react-modal": ">=3"
|
||||
}
|
||||
},
|
||||
"node_modules/@ast-grep/napi": {
|
||||
"version": "0.37.0",
|
||||
"resolved": "https://registry.npmjs.org/@ast-grep/napi/-/napi-0.37.0.tgz",
|
||||
"integrity": "sha512-Hb4o6h1Pf6yRUAX07DR4JVY7dmQw+RVQMW5/m55GoiAT/VRoKCWBtIUPPOnqDVhbx1Cjfil9b6EDrgJsUAujEQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@ast-grep/napi-darwin-arm64": "0.37.0",
|
||||
"@ast-grep/napi-darwin-x64": "0.37.0",
|
||||
"@ast-grep/napi-linux-arm64-gnu": "0.37.0",
|
||||
"@ast-grep/napi-linux-arm64-musl": "0.37.0",
|
||||
"@ast-grep/napi-linux-x64-gnu": "0.37.0",
|
||||
"@ast-grep/napi-linux-x64-musl": "0.37.0",
|
||||
"@ast-grep/napi-win32-arm64-msvc": "0.37.0",
|
||||
"@ast-grep/napi-win32-ia32-msvc": "0.37.0",
|
||||
"@ast-grep/napi-win32-x64-msvc": "0.37.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@ast-grep/napi-darwin-arm64": {
|
||||
"version": "0.37.0",
|
||||
"resolved": "https://registry.npmjs.org/@ast-grep/napi-darwin-arm64/-/napi-darwin-arm64-0.37.0.tgz",
|
||||
"integrity": "sha512-QAiIiaAbLvMEg/yBbyKn+p1gX2/FuaC0SMf7D7capm/oG4xGMzdeaQIcSosF4TCxxV+hIH4Bz9e4/u7w6Bnk3Q==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@ast-grep/napi-darwin-x64": {
|
||||
"version": "0.37.0",
|
||||
"resolved": "https://registry.npmjs.org/@ast-grep/napi-darwin-x64/-/napi-darwin-x64-0.37.0.tgz",
|
||||
"integrity": "sha512-zvcvdgekd4ySV3zUbUp8HF5nk5zqwiMXTuVzTUdl/w08O7JjM6XPOIVT+d2o/MqwM9rsXdzdergY5oY2RdhSPA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@ast-grep/napi-linux-arm64-gnu": {
|
||||
"version": "0.37.0",
|
||||
"resolved": "https://registry.npmjs.org/@ast-grep/napi-linux-arm64-gnu/-/napi-linux-arm64-gnu-0.37.0.tgz",
|
||||
"integrity": "sha512-L7Sj0lXy8X+BqSMgr1LB8cCoWk0rericdeu+dC8/c8zpsav5Oo2IQKY1PmiZ7H8IHoFBbURLf8iklY9wsD+cyA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@ast-grep/napi-linux-arm64-musl": {
|
||||
"version": "0.37.0",
|
||||
"resolved": "https://registry.npmjs.org/@ast-grep/napi-linux-arm64-musl/-/napi-linux-arm64-musl-0.37.0.tgz",
|
||||
"integrity": "sha512-LF9sAvYy6es/OdyJDO3RwkX3I82Vkfsng1sqUBcoWC1jVb1wX5YVzHtpQox9JrEhGl+bNp7FYxB4Qba9OdA5GA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@ast-grep/napi-linux-x64-gnu": {
|
||||
"version": "0.37.0",
|
||||
"resolved": "https://registry.npmjs.org/@ast-grep/napi-linux-x64-gnu/-/napi-linux-x64-gnu-0.37.0.tgz",
|
||||
"integrity": "sha512-TViz5/klqre6aSmJzswEIjApnGjJzstG/SE8VDWsrftMBMYt2PTu3MeluZVwzSqDao8doT/P+6U11dU05UOgxw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@ast-grep/napi-linux-x64-musl": {
|
||||
"version": "0.37.0",
|
||||
"resolved": "https://registry.npmjs.org/@ast-grep/napi-linux-x64-musl/-/napi-linux-x64-musl-0.37.0.tgz",
|
||||
"integrity": "sha512-/BcCH33S9E3ovOAEoxYngUNXgb+JLg991sdyiNP2bSoYd30a9RHrG7CYwW6fMgua3ijQ474eV6cq9yZO1bCpXg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@ast-grep/napi-win32-arm64-msvc": {
|
||||
"version": "0.37.0",
|
||||
"resolved": "https://registry.npmjs.org/@ast-grep/napi-win32-arm64-msvc/-/napi-win32-arm64-msvc-0.37.0.tgz",
|
||||
"integrity": "sha512-TjQA4cFoIEW2bgjLkaL9yqT4XWuuLa5MCNd0VCDhGRDMNQ9+rhwi9eLOWRaap3xzT7g+nlbcEHL3AkVCD2+b3A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@ast-grep/napi-win32-ia32-msvc": {
|
||||
"version": "0.37.0",
|
||||
"resolved": "https://registry.npmjs.org/@ast-grep/napi-win32-ia32-msvc/-/napi-win32-ia32-msvc-0.37.0.tgz",
|
||||
"integrity": "sha512-uNmVka8fJCdYsyOlF9aZqQMLTatEYBynjChVTzUfFMDfmZ0bihs/YTqJVbkSm8TZM7CUX82apvn50z/dX5iWRA==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@ast-grep/napi-win32-x64-msvc": {
|
||||
"version": "0.37.0",
|
||||
"resolved": "https://registry.npmjs.org/@ast-grep/napi-win32-x64-msvc/-/napi-win32-x64-msvc-0.37.0.tgz",
|
||||
"integrity": "sha512-vCiFOT3hSCQuHHfZ933GAwnPzmL0G04JxQEsBRfqONywyT8bSdDc/ECpAfr3S9VcS4JZ9/F6tkePKW/Om2Dq2g==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/msal-browser": {
|
||||
"version": "3.28.1",
|
||||
"resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-3.28.1.tgz",
|
||||
@@ -489,6 +665,40 @@
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@emnapi/core": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.5.0.tgz",
|
||||
"integrity": "sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@emnapi/wasi-threads": "1.1.0",
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@emnapi/runtime": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.5.0.tgz",
|
||||
"integrity": "sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@emnapi/wasi-threads": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz",
|
||||
"integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@emotion/babel-plugin": {
|
||||
"version": "11.12.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.12.0.tgz",
|
||||
@@ -1074,6 +1284,78 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@module-federation/error-codes": {
|
||||
"version": "0.18.0",
|
||||
"resolved": "https://registry.npmjs.org/@module-federation/error-codes/-/error-codes-0.18.0.tgz",
|
||||
"integrity": "sha512-Woonm8ehyVIUPXChmbu80Zj6uJkC0dD9SJUZ/wOPtO8iiz/m+dkrOugAuKgoiR6qH4F+yorWila954tBz4uKsQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@module-federation/runtime": {
|
||||
"version": "0.18.0",
|
||||
"resolved": "https://registry.npmjs.org/@module-federation/runtime/-/runtime-0.18.0.tgz",
|
||||
"integrity": "sha512-+C4YtoSztM7nHwNyZl6dQKGUVJdsPrUdaf3HIKReg/GQbrt9uvOlUWo2NXMZ8vDAnf/QRrpSYAwXHmWDn9Obaw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@module-federation/error-codes": "0.18.0",
|
||||
"@module-federation/runtime-core": "0.18.0",
|
||||
"@module-federation/sdk": "0.18.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@module-federation/runtime-core": {
|
||||
"version": "0.18.0",
|
||||
"resolved": "https://registry.npmjs.org/@module-federation/runtime-core/-/runtime-core-0.18.0.tgz",
|
||||
"integrity": "sha512-ZyYhrDyVAhUzriOsVfgL6vwd+5ebYm595Y13KeMf6TKDRoUHBMTLGQ8WM4TDj8JNsy7LigncK8C03fn97of0QQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@module-federation/error-codes": "0.18.0",
|
||||
"@module-federation/sdk": "0.18.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@module-federation/runtime-tools": {
|
||||
"version": "0.18.0",
|
||||
"resolved": "https://registry.npmjs.org/@module-federation/runtime-tools/-/runtime-tools-0.18.0.tgz",
|
||||
"integrity": "sha512-fSga9o4t1UfXNV/Kh6qFvRyZpPp3EHSPRISNeyT8ZoTpzDNiYzhtw0BPUSSD8m6C6XQh2s/11rI4g80UY+d+hA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@module-federation/runtime": "0.18.0",
|
||||
"@module-federation/webpack-bundler-runtime": "0.18.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@module-federation/sdk": {
|
||||
"version": "0.18.0",
|
||||
"resolved": "https://registry.npmjs.org/@module-federation/sdk/-/sdk-0.18.0.tgz",
|
||||
"integrity": "sha512-Lo/Feq73tO2unjmpRfyyoUkTVoejhItXOk/h5C+4cistnHbTV8XHrW/13fD5e1Iu60heVdAhhelJd6F898Ve9A==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@module-federation/webpack-bundler-runtime": {
|
||||
"version": "0.18.0",
|
||||
"resolved": "https://registry.npmjs.org/@module-federation/webpack-bundler-runtime/-/webpack-bundler-runtime-0.18.0.tgz",
|
||||
"integrity": "sha512-TEvErbF+YQ+6IFimhUYKK3a5wapD90d90sLsNpcu2kB3QGT7t4nIluE25duXuZDVUKLz86tEPrza/oaaCWTpvQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@module-federation/runtime": "0.18.0",
|
||||
"@module-federation/sdk": "0.18.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@napi-rs/wasm-runtime": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.0.6.tgz",
|
||||
"integrity": "sha512-DXj75ewm11LIWUk198QSKUTxjyRjsBwk09MuMk5DGK+GDUtyPhhEHOGP/Xwwj3DjQXXkivoBirmOnKrLfc0+9g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@emnapi/core": "^1.5.0",
|
||||
"@emnapi/runtime": "^1.5.0",
|
||||
"@tybys/wasm-util": "^0.10.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@notesnook-importer/core": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@notesnook-importer/core/-/core-2.2.2.tgz",
|
||||
@@ -1445,6 +1727,247 @@
|
||||
"win32"
|
||||
]
|
||||
},
|
||||
"node_modules/@rsbuild/core": {
|
||||
"version": "1.5.13",
|
||||
"resolved": "https://registry.npmjs.org/@rsbuild/core/-/core-1.5.13.tgz",
|
||||
"integrity": "sha512-P+TCvZCVpBYZ3GDdnzR/tZKicE41khJIqIRlJYnEc9dwUfX1/eqRf8lA8yrsbB5iZbSfj1iOoH1N25cCQ3hhuA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@rspack/core": "1.5.8",
|
||||
"@rspack/lite-tapable": "~1.0.1",
|
||||
"@swc/helpers": "^0.5.17",
|
||||
"core-js": "~3.45.1",
|
||||
"jiti": "^2.6.0"
|
||||
},
|
||||
"bin": {
|
||||
"rsbuild": "bin/rsbuild.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rslib/core": {
|
||||
"version": "0.15.0",
|
||||
"resolved": "https://registry.npmjs.org/@rslib/core/-/core-0.15.0.tgz",
|
||||
"integrity": "sha512-ZxDwEU38qsM32VpW4Dy7oNf6MxgrRAgYxVsuv8v+aKm4pIOSs0nzDTJHsa2vhKjFdyRtgnHObZ6pjoJ0cmcU3A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@rsbuild/core": "~1.5.13",
|
||||
"rsbuild-plugin-dts": "0.15.0"
|
||||
},
|
||||
"bin": {
|
||||
"rslib": "bin/rslib.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.12.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@microsoft/api-extractor": "^7",
|
||||
"typescript": "^5"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@microsoft/api-extractor": {
|
||||
"optional": true
|
||||
},
|
||||
"typescript": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@rspack/binding": {
|
||||
"version": "1.5.8",
|
||||
"resolved": "https://registry.npmjs.org/@rspack/binding/-/binding-1.5.8.tgz",
|
||||
"integrity": "sha512-/91CzhRl9r5BIQCgGsS7jA6MDbw1I2BQpbfcUUdkdKl2P79K3Zo/Mw/TvKzS86catwLaUQEgkGRmYawOfPg7ow==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optionalDependencies": {
|
||||
"@rspack/binding-darwin-arm64": "1.5.8",
|
||||
"@rspack/binding-darwin-x64": "1.5.8",
|
||||
"@rspack/binding-linux-arm64-gnu": "1.5.8",
|
||||
"@rspack/binding-linux-arm64-musl": "1.5.8",
|
||||
"@rspack/binding-linux-x64-gnu": "1.5.8",
|
||||
"@rspack/binding-linux-x64-musl": "1.5.8",
|
||||
"@rspack/binding-wasm32-wasi": "1.5.8",
|
||||
"@rspack/binding-win32-arm64-msvc": "1.5.8",
|
||||
"@rspack/binding-win32-ia32-msvc": "1.5.8",
|
||||
"@rspack/binding-win32-x64-msvc": "1.5.8"
|
||||
}
|
||||
},
|
||||
"node_modules/@rspack/binding-darwin-arm64": {
|
||||
"version": "1.5.8",
|
||||
"resolved": "https://registry.npmjs.org/@rspack/binding-darwin-arm64/-/binding-darwin-arm64-1.5.8.tgz",
|
||||
"integrity": "sha512-spJfpOSN3f7V90ic45/ET2NKB2ujAViCNmqb0iGurMNQtFRq+7Kd+jvVKKGXKBHBbsQrFhidSWbbqy2PBPGK8g==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
]
|
||||
},
|
||||
"node_modules/@rspack/binding-darwin-x64": {
|
||||
"version": "1.5.8",
|
||||
"resolved": "https://registry.npmjs.org/@rspack/binding-darwin-x64/-/binding-darwin-x64-1.5.8.tgz",
|
||||
"integrity": "sha512-YFOzeL1IBknBcri8vjUp43dfUBylCeQnD+9O9p0wZmLAw7DtpN5JEOe2AkGo8kdTqJjYKI+cczJPKIw6lu1LWw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
]
|
||||
},
|
||||
"node_modules/@rspack/binding-linux-arm64-gnu": {
|
||||
"version": "1.5.8",
|
||||
"resolved": "https://registry.npmjs.org/@rspack/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.5.8.tgz",
|
||||
"integrity": "sha512-UAWCsOnpkvy8eAVRo0uipbHXDhnoDq5zmqWTMhpga0/a3yzCp2e+fnjZb/qnFNYb5MeL0O1mwMOYgn1M3oHILQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rspack/binding-linux-arm64-musl": {
|
||||
"version": "1.5.8",
|
||||
"resolved": "https://registry.npmjs.org/@rspack/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.5.8.tgz",
|
||||
"integrity": "sha512-GnSvGT4GjokPSD45cTtE+g7LgghuxSP1MRmvd+Vp/I8pnxTVSTsebRod4TAqyiv+l11nuS8yqNveK9qiOkBLWw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rspack/binding-linux-x64-gnu": {
|
||||
"version": "1.5.8",
|
||||
"resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.5.8.tgz",
|
||||
"integrity": "sha512-XLxh5n/pzUfxsugz/8rVBv+Tx2nqEM+9rharK69kfooDsQNKyz7PANllBQ/v4svJ+W0BRHnDL4qXSGdteZeEjA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rspack/binding-linux-x64-musl": {
|
||||
"version": "1.5.8",
|
||||
"resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-musl/-/binding-linux-x64-musl-1.5.8.tgz",
|
||||
"integrity": "sha512-gE0+MZmwF+01p9/svpEESkzkLpBkVUG2o03YMpwXYC/maeRRhWvF8BJ7R3i/Ls/jFGSE87dKX5NbRLVzqksq/w==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rspack/binding-wasm32-wasi": {
|
||||
"version": "1.5.8",
|
||||
"resolved": "https://registry.npmjs.org/@rspack/binding-wasm32-wasi/-/binding-wasm32-wasi-1.5.8.tgz",
|
||||
"integrity": "sha512-cfg3niNHeJuxuml1Vy9VvaJrI/5TakzoaZvKX2g5S24wfzR50Eyy4JAsZ+L2voWQQp1yMJbmPYPmnTCTxdJQBQ==",
|
||||
"cpu": [
|
||||
"wasm32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@napi-rs/wasm-runtime": "^1.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@rspack/binding-win32-arm64-msvc": {
|
||||
"version": "1.5.8",
|
||||
"resolved": "https://registry.npmjs.org/@rspack/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.5.8.tgz",
|
||||
"integrity": "sha512-7i3ZTHFXKfU/9Jm9XhpMkrdkxO7lfeYMNVEGkuU5dyBfRMQj69dRgPL7zJwc2plXiqu9LUOl+TwDNTjap7Q36g==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
]
|
||||
},
|
||||
"node_modules/@rspack/binding-win32-ia32-msvc": {
|
||||
"version": "1.5.8",
|
||||
"resolved": "https://registry.npmjs.org/@rspack/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-1.5.8.tgz",
|
||||
"integrity": "sha512-7ZPPWO11J+soea1+mnfaPpQt7GIodBM7A86dx6PbXgVEoZmetcWPrCF2NBfXxQWOKJ9L3RYltC4z+ZyXRgMOrw==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
]
|
||||
},
|
||||
"node_modules/@rspack/binding-win32-x64-msvc": {
|
||||
"version": "1.5.8",
|
||||
"resolved": "https://registry.npmjs.org/@rspack/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.5.8.tgz",
|
||||
"integrity": "sha512-N/zXQgzIxME3YUzXT8qnyzxjqcnXudWOeDh8CAG9zqTCnCiy16SFfQ/cQgEoLlD9geQntV6jx2GbDDI5kpDGMQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
]
|
||||
},
|
||||
"node_modules/@rspack/core": {
|
||||
"version": "1.5.8",
|
||||
"resolved": "https://registry.npmjs.org/@rspack/core/-/core-1.5.8.tgz",
|
||||
"integrity": "sha512-sUd2LfiDhqYVfvknuoz0+/c+wSpn693xotnG5g1CSWKZArbtwiYzBIVnNlcHGmuoBRsnj/TkSq8dTQ7gwfBroQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@module-federation/runtime-tools": "0.18.0",
|
||||
"@rspack/binding": "1.5.8",
|
||||
"@rspack/lite-tapable": "1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.12.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@swc/helpers": ">=0.5.1"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@swc/helpers": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@rspack/lite-tapable": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@rspack/lite-tapable/-/lite-tapable-1.0.1.tgz",
|
||||
"integrity": "sha512-VynGOEsVw2s8TAlLf/uESfrgfrq2+rcXB1muPJYBWbsm1Oa6r5qVQhjA5ggM6z/coYPrsVMgovl3Ff7Q7OCp1w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@social-embed/lib": {
|
||||
"version": "0.1.0-next.7",
|
||||
"resolved": "https://registry.npmjs.org/@social-embed/lib/-/lib-0.1.0-next.7.tgz",
|
||||
@@ -1755,6 +2278,49 @@
|
||||
"@styled-system/css": "^5.1.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/helpers": {
|
||||
"version": "0.5.17",
|
||||
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz",
|
||||
"integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"tslib": "^2.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@theme-ui/color-modes": {
|
||||
"version": "0.16.2",
|
||||
"resolved": "https://registry.npmjs.org/@theme-ui/color-modes/-/color-modes-0.16.2.tgz",
|
||||
"integrity": "sha512-jWEWx53lxNgWCT38i/kwLV2rsvJz8lVZgi5oImnVwYba9VejXD23q1ckbNFJHosQ8KKXY87ht0KPC6BQFIiHtQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@theme-ui/core": "^0.16.2",
|
||||
"@theme-ui/css": "^0.16.2",
|
||||
"deepmerge": "^4.2.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@emotion/react": "^11.11.1",
|
||||
"react": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@theme-ui/color-modes/node_modules/@theme-ui/core": {
|
||||
"version": "0.16.2",
|
||||
"resolved": "https://registry.npmjs.org/@theme-ui/core/-/core-0.16.2.tgz",
|
||||
"integrity": "sha512-bBd/ltbwO9vIUjF1jtlOX6XN0IIOdf1vzBp2JCKsSOqdfn84m+XL8OogIe/zOhQ+aM94Nrq4+32tFJc8sFav4Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@theme-ui/css": "^0.16.2",
|
||||
"deepmerge": "^4.2.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@emotion/react": "^11.11.1",
|
||||
"react": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@theme-ui/components": {
|
||||
"version": "0.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@theme-ui/components/-/components-0.16.1.tgz",
|
||||
@@ -1800,6 +2366,39 @@
|
||||
"@emotion/react": "^11.11.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@theme-ui/theme-provider": {
|
||||
"version": "0.16.2",
|
||||
"resolved": "https://registry.npmjs.org/@theme-ui/theme-provider/-/theme-provider-0.16.2.tgz",
|
||||
"integrity": "sha512-LRnVevODcGqO0JyLJ3wht+PV3ZoZcJ7XXLJAJWDoGeII4vZcPQKwVy4Lpz/juHsZppQxKcB3U+sQDGBnP25irQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@theme-ui/color-modes": "^0.16.2",
|
||||
"@theme-ui/core": "^0.16.2",
|
||||
"@theme-ui/css": "^0.16.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@emotion/react": "^11.11.1",
|
||||
"react": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@theme-ui/theme-provider/node_modules/@theme-ui/core": {
|
||||
"version": "0.16.2",
|
||||
"resolved": "https://registry.npmjs.org/@theme-ui/core/-/core-0.16.2.tgz",
|
||||
"integrity": "sha512-bBd/ltbwO9vIUjF1jtlOX6XN0IIOdf1vzBp2JCKsSOqdfn84m+XL8OogIe/zOhQ+aM94Nrq4+32tFJc8sFav4Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@theme-ui/css": "^0.16.2",
|
||||
"deepmerge": "^4.2.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@emotion/react": "^11.11.1",
|
||||
"react": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/core": {
|
||||
"version": "2.6.6",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/core/-/core-2.6.6.tgz",
|
||||
@@ -2291,6 +2890,17 @@
|
||||
"@tiptap/core": "^2.6.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@tybys/wasm-util": {
|
||||
"version": "0.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz",
|
||||
"integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/debug": {
|
||||
"version": "4.1.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz",
|
||||
@@ -2922,6 +3532,18 @@
|
||||
"integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/core-js": {
|
||||
"version": "3.45.1",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.45.1.tgz",
|
||||
"integrity": "sha512-L4NPsJlCfZsPeXukyzHFlg/i7IIVwHSItR0wg0FLNqYClJ4MQYTYLbC7EkjKYRLZF2iof2MUgN0EGy7MdQFChg==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/core-js"
|
||||
}
|
||||
},
|
||||
"node_modules/cosmiconfig": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
|
||||
@@ -3154,6 +3776,17 @@
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/encoding": {
|
||||
"version": "0.1.13",
|
||||
"resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz",
|
||||
"integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"iconv-lite": "^0.6.2"
|
||||
}
|
||||
},
|
||||
"node_modules/end-of-stream": {
|
||||
"version": "1.4.4",
|
||||
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
|
||||
@@ -3638,6 +4271,20 @@
|
||||
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/iconv-lite": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ieee754": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
|
||||
@@ -3865,11 +4512,20 @@
|
||||
"whatwg-fetch": "^3.4.1"
|
||||
}
|
||||
},
|
||||
"node_modules/jiti": {
|
||||
"version": "2.6.1",
|
||||
"resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz",
|
||||
"integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"jiti": "lib/jiti-cli.mjs"
|
||||
}
|
||||
},
|
||||
"node_modules/js-tokens": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
|
||||
"dev": true
|
||||
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
|
||||
},
|
||||
"node_modules/js-yaml": {
|
||||
"version": "3.14.1",
|
||||
@@ -4085,7 +4741,6 @@
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
||||
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"js-tokens": "^3.0.0 || ^4.0.0"
|
||||
},
|
||||
@@ -5437,6 +6092,16 @@
|
||||
"resolved": "https://registry.npmjs.org/prism-themes/-/prism-themes-1.9.0.tgz",
|
||||
"integrity": "sha512-tX2AYsehKDw1EORwBps+WhBFKc2kxfoFpQAjxBndbZKr4fRmMkv47XN0BghC/K1qwodB1otbe4oF23vUTFDokw=="
|
||||
},
|
||||
"node_modules/prismjs": {
|
||||
"version": "1.30.0",
|
||||
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz",
|
||||
"integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/prop-types": {
|
||||
"version": "15.8.1",
|
||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
||||
@@ -5708,7 +6373,6 @@
|
||||
"version": "18.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
|
||||
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0"
|
||||
},
|
||||
@@ -5729,7 +6393,6 @@
|
||||
"version": "18.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
|
||||
"integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"scheduler": "^0.23.2"
|
||||
@@ -6042,6 +6705,36 @@
|
||||
"resolved": "https://registry.npmjs.org/rope-sequence/-/rope-sequence-1.3.4.tgz",
|
||||
"integrity": "sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ=="
|
||||
},
|
||||
"node_modules/rsbuild-plugin-dts": {
|
||||
"version": "0.15.0",
|
||||
"resolved": "https://registry.npmjs.org/rsbuild-plugin-dts/-/rsbuild-plugin-dts-0.15.0.tgz",
|
||||
"integrity": "sha512-qKGCtv4fxKxWZb3DxHpgmP6uY+JAckovBE/92APerORvrtEtqwY6sAEY2UOaRo2ErENOiWhf5yFLE5EhdUsj9A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@ast-grep/napi": "0.37.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.12.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@microsoft/api-extractor": "^7",
|
||||
"@rsbuild/core": "1.x",
|
||||
"@typescript/native-preview": "7.x",
|
||||
"typescript": "^5"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@microsoft/api-extractor": {
|
||||
"optional": true
|
||||
},
|
||||
"@typescript/native-preview": {
|
||||
"optional": true
|
||||
},
|
||||
"typescript": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/sade": {
|
||||
"version": "1.8.1",
|
||||
"resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz",
|
||||
@@ -6083,7 +6776,6 @@
|
||||
"version": "0.23.2",
|
||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
|
||||
"integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0"
|
||||
}
|
||||
@@ -6480,9 +7172,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "2.6.2",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
|
||||
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||
"license": "0BSD"
|
||||
},
|
||||
"node_modules/tunnel-agent": {
|
||||
"version": "0.6.0",
|
||||
|
||||
@@ -97,6 +97,7 @@
|
||||
"happy-dom": "16.0.1",
|
||||
"isomorphic-fetch": "^3.0.0",
|
||||
"nodemon": "^3.1.7",
|
||||
"prismjs": "^1.30.0",
|
||||
"prosemirror-test-builder": "^1.1.1",
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.3.1",
|
||||
|
||||
@@ -23,15 +23,16 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import fs from "fs";
|
||||
import { readFile } from "fs/promises";
|
||||
import path from "path";
|
||||
|
||||
export async function langen(rootDirectory) {
|
||||
const response = await fetch(
|
||||
"https://github.com/PrismJS/prism/raw/master/components.json"
|
||||
const json = JSON.parse(
|
||||
await readFile(
|
||||
path.join(rootDirectory, "node_modules", "prismjs", "components.json"),
|
||||
"utf-8"
|
||||
)
|
||||
);
|
||||
if (!response.ok) return {};
|
||||
|
||||
const json = await response.json();
|
||||
let languages = [];
|
||||
for (const key in json.languages) {
|
||||
if (key === "meta") continue;
|
||||
|
||||
@@ -28,6 +28,7 @@ import {
|
||||
import { useMemo } from "react";
|
||||
import { CodeBlock } from "../../extensions/code-block/index.js";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { keybindings } from "@notesnook/common";
|
||||
|
||||
const defaultLevels = [1, 2, 3, 4, 5, 6] as const;
|
||||
|
||||
@@ -70,7 +71,7 @@ function toMenuItems(
|
||||
key: `heading-${level}`,
|
||||
title: toolbarLocation === "bottom" ? `H${level}` : strings.heading(level),
|
||||
isChecked: level === currentHeadingLevel,
|
||||
modifier: `Mod-Alt-${level}`,
|
||||
modifier: keybindings[`insertHeading${level}`].keys,
|
||||
onClick: () =>
|
||||
editor
|
||||
?.chain()
|
||||
@@ -84,7 +85,7 @@ function toMenuItems(
|
||||
type: "button",
|
||||
title: strings.paragraph(),
|
||||
isChecked: !currentHeadingLevel,
|
||||
modifier: `Mod-Alt-0`,
|
||||
modifier: keybindings.insertParagraph.keys,
|
||||
onClick: () => editor.chain().focus().setParagraph().run()
|
||||
};
|
||||
return [paragraph, ...menuItems];
|
||||
|
||||
@@ -3795,8 +3795,8 @@ msgstr "Member since {date}"
|
||||
msgid "Merge cells"
|
||||
msgstr "Merge cells"
|
||||
|
||||
#. placeholder {0}: progress ? `${progress?.collection}` : null
|
||||
#. placeholder {1}: progress ? `(${progress.current}/${progress.total}) ` : null
|
||||
#. placeholder {0}: progress ? `${progress?.collection}` : ""
|
||||
#. placeholder {1}: progress ? `(${progress.current}/${progress.total}) ` : ""
|
||||
#: src/strings.ts:246
|
||||
msgid "Migrating {0} {1}... please wait"
|
||||
msgstr "Migrating {0} {1}... please wait"
|
||||
|
||||
@@ -3775,8 +3775,8 @@ msgstr ""
|
||||
msgid "Merge cells"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: progress ? `${progress?.collection}` : null
|
||||
#. placeholder {1}: progress ? `(${progress.current}/${progress.total}) ` : null
|
||||
#. placeholder {0}: progress ? `${progress?.collection}` : ""
|
||||
#. placeholder {1}: progress ? `(${progress.current}/${progress.total}) ` : ""
|
||||
#: src/strings.ts:246
|
||||
msgid "Migrating {0} {1}... please wait"
|
||||
msgstr ""
|
||||
|
||||
@@ -243,8 +243,8 @@ export const strings = {
|
||||
collection: string;
|
||||
current: number;
|
||||
}) =>
|
||||
t`Migrating ${progress ? `${progress?.collection}` : null} ${
|
||||
progress ? `(${progress.current}/${progress.total}) ` : null
|
||||
t`Migrating ${progress ? `${progress?.collection}` : ""} ${
|
||||
progress ? `(${progress.current}/${progress.total}) ` : ""
|
||||
}... please wait`,
|
||||
migrationError: () =>
|
||||
t`An error occurred while migrating your data. You can logout of your account and try to relogin. However this is not recommended as it may result in some data loss if your data was not synced.`,
|
||||
@@ -1791,7 +1791,7 @@ For example:
|
||||
],
|
||||
shareWithFriendsDesc: () => t`Because where's the fun in nookin' alone?`,
|
||||
notebooksAllCaps: () => t`NOTEBOOKS`,
|
||||
authenticatedAs: (email?: string) => t`Authenticated as ${email}`,
|
||||
authenticatedAs: (email: string) => t`Authenticated as ${email}`,
|
||||
rememberedYourPassword: () => t`Remembered your password?`,
|
||||
chooseRecoveryMethod: () => t`Choose a recovery method`,
|
||||
chooseRecoveryMethodDesc: () => t`How do you want to recover your account?`,
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
// vite.config.mts
|
||||
import path, { resolve } from "path";
|
||||
import { defineConfig } from "file:///Users/thecodrr/Sources/Repos/notesnook/packages/intl/node_modules/vite/dist/node/index.js";
|
||||
import swc from "file:///Users/thecodrr/Sources/Repos/notesnook/packages/intl/node_modules/vite-plugin-swc-transform/dist/esm/index.js";
|
||||
import dts from "file:///Users/thecodrr/Sources/Repos/notesnook/packages/intl/node_modules/vite-plugin-dts/dist/index.mjs";
|
||||
import { fileURLToPath } from "url";
|
||||
var __vite_injected_original_import_meta_url = "file:///Users/thecodrr/Sources/Repos/notesnook/packages/intl/vite.config.mts";
|
||||
var __filename = fileURLToPath(__vite_injected_original_import_meta_url);
|
||||
var __dirname = path.dirname(__filename);
|
||||
var vite_config_default = defineConfig({
|
||||
plugins: [
|
||||
swc({
|
||||
swcOptions: {
|
||||
sourceMaps: true,
|
||||
jsc: {
|
||||
parser: {
|
||||
syntax: "typescript",
|
||||
tsx: true
|
||||
},
|
||||
baseUrl: __dirname,
|
||||
paths: {
|
||||
"$src/*": ["src/*"]
|
||||
},
|
||||
experimental: {
|
||||
plugins: [
|
||||
[
|
||||
"@lingui/swc-plugin",
|
||||
{
|
||||
runtimeModules: {
|
||||
i18n: ["$src/setup", "i18n"]
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}),
|
||||
dts({
|
||||
exclude: ["**/locales/*.json"],
|
||||
rollupTypes: true
|
||||
})
|
||||
],
|
||||
build: {
|
||||
lib: {
|
||||
entry: resolve(__dirname, "src/index.ts"),
|
||||
formats: ["cjs", "es"],
|
||||
fileName(format) {
|
||||
return format === "cjs" ? "index.js" : "index.mjs";
|
||||
}
|
||||
},
|
||||
outDir: resolve(__dirname, "dist")
|
||||
}
|
||||
});
|
||||
export {
|
||||
vite_config_default as default
|
||||
};
|
||||
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcubXRzIl0sCiAgInNvdXJjZXNDb250ZW50IjogWyJjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfZGlybmFtZSA9IFwiL1VzZXJzL3RoZWNvZHJyL1NvdXJjZXMvUmVwb3Mvbm90ZXNub29rL3BhY2thZ2VzL2ludGxcIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfZmlsZW5hbWUgPSBcIi9Vc2Vycy90aGVjb2Ryci9Tb3VyY2VzL1JlcG9zL25vdGVzbm9vay9wYWNrYWdlcy9pbnRsL3ZpdGUuY29uZmlnLm10c1wiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9pbXBvcnRfbWV0YV91cmwgPSBcImZpbGU6Ly8vVXNlcnMvdGhlY29kcnIvU291cmNlcy9SZXBvcy9ub3Rlc25vb2svcGFja2FnZXMvaW50bC92aXRlLmNvbmZpZy5tdHNcIjsvKlxuVGhpcyBmaWxlIGlzIHBhcnQgb2YgdGhlIE5vdGVzbm9vayBwcm9qZWN0IChodHRwczovL25vdGVzbm9vay5jb20vKVxuXG5Db3B5cmlnaHQgKEMpIDIwMjMgU3RyZWV0d3JpdGVycyAoUHJpdmF0ZSkgTGltaXRlZFxuXG5UaGlzIHByb2dyYW0gaXMgZnJlZSBzb2Z0d2FyZTogeW91IGNhbiByZWRpc3RyaWJ1dGUgaXQgYW5kL29yIG1vZGlmeVxuaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBhcyBwdWJsaXNoZWQgYnlcbnRoZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb24sIGVpdGhlciB2ZXJzaW9uIDMgb2YgdGhlIExpY2Vuc2UsIG9yXG4oYXQgeW91ciBvcHRpb24pIGFueSBsYXRlciB2ZXJzaW9uLlxuXG5UaGlzIHByb2dyYW0gaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCxcbmJ1dCBXSVRIT1VUIEFOWSBXQVJSQU5UWTsgd2l0aG91dCBldmVuIHRoZSBpbXBsaWVkIHdhcnJhbnR5IG9mXG5NRVJDSEFOVEFCSUxJVFkgb3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlXG5HTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLlxuXG5Zb3Ugc2hvdWxkIGhhdmUgcmVjZWl2ZWQgYSBjb3B5IG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZVxuYWxvbmcgd2l0aCB0aGlzIHByb2dyYW0uICBJZiBub3QsIHNlZSA8aHR0cDovL3d3dy5nbnUub3JnL2xpY2Vuc2VzLz4uXG4qL1xuXG5pbXBvcnQgcGF0aCwgeyByZXNvbHZlIH0gZnJvbSBcInBhdGhcIjtcbmltcG9ydCB7IGRlZmluZUNvbmZpZyB9IGZyb20gXCJ2aXRlXCI7XG5pbXBvcnQgc3djIGZyb20gXCJ2aXRlLXBsdWdpbi1zd2MtdHJhbnNmb3JtXCI7XG5pbXBvcnQgZHRzIGZyb20gXCJ2aXRlLXBsdWdpbi1kdHNcIjtcbmltcG9ydCB7IGZpbGVVUkxUb1BhdGggfSBmcm9tIFwidXJsXCI7XG5cbmNvbnN0IF9fZmlsZW5hbWUgPSBmaWxlVVJMVG9QYXRoKGltcG9ydC5tZXRhLnVybCk7XG5jb25zdCBfX2Rpcm5hbWUgPSBwYXRoLmRpcm5hbWUoX19maWxlbmFtZSk7XG5cbmV4cG9ydCBkZWZhdWx0IGRlZmluZUNvbmZpZyh7XG4gIHBsdWdpbnM6IFtcbiAgICBzd2Moe1xuICAgICAgc3djT3B0aW9uczoge1xuICAgICAgICBzb3VyY2VNYXBzOiB0cnVlLFxuICAgICAgICBqc2M6IHtcbiAgICAgICAgICBwYXJzZXI6IHtcbiAgICAgICAgICAgIHN5bnRheDogXCJ0eXBlc2NyaXB0XCIsXG4gICAgICAgICAgICB0c3g6IHRydWVcbiAgICAgICAgICB9LFxuICAgICAgICAgIGJhc2VVcmw6IF9fZGlybmFtZSxcbiAgICAgICAgICBwYXRoczoge1xuICAgICAgICAgICAgXCIkc3JjLypcIjogW1wic3JjLypcIl1cbiAgICAgICAgICB9LFxuICAgICAgICAgIGV4cGVyaW1lbnRhbDoge1xuICAgICAgICAgICAgcGx1Z2luczogW1xuICAgICAgICAgICAgICBbXG4gICAgICAgICAgICAgICAgXCJAbGluZ3VpL3N3Yy1wbHVnaW5cIixcbiAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICBydW50aW1lTW9kdWxlczoge1xuICAgICAgICAgICAgICAgICAgICBpMThuOiBbXCIkc3JjL3NldHVwXCIsIFwiaTE4blwiXVxuICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgXVxuICAgICAgICAgICAgXVxuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuICAgIH0pLFxuICAgIGR0cyh7XG4gICAgICBleGNsdWRlOiBbXCIqKi9sb2NhbGVzLyouanNvblwiXSxcbiAgICAgIHJvbGx1cFR5cGVzOiB0cnVlXG4gICAgfSlcbiAgXSxcbiAgYnVpbGQ6IHtcbiAgICBsaWI6IHtcbiAgICAgIGVudHJ5OiByZXNvbHZlKF9fZGlybmFtZSwgXCJzcmMvaW5kZXgudHNcIiksXG4gICAgICBmb3JtYXRzOiBbXCJjanNcIiwgXCJlc1wiXSxcbiAgICAgIGZpbGVOYW1lKGZvcm1hdCkge1xuICAgICAgICByZXR1cm4gZm9ybWF0ID09PSBcImNqc1wiID8gXCJpbmRleC5qc1wiIDogXCJpbmRleC5tanNcIjtcbiAgICAgIH1cbiAgICB9LFxuICAgIG91dERpcjogcmVzb2x2ZShfX2Rpcm5hbWUsIFwiZGlzdFwiKVxuICB9XG59KTtcbiJdLAogICJtYXBwaW5ncyI6ICI7QUFtQkEsT0FBTyxRQUFRLGVBQWU7QUFDOUIsU0FBUyxvQkFBb0I7QUFDN0IsT0FBTyxTQUFTO0FBQ2hCLE9BQU8sU0FBUztBQUNoQixTQUFTLHFCQUFxQjtBQXZCcUwsSUFBTSwyQ0FBMkM7QUF5QnBRLElBQU0sYUFBYSxjQUFjLHdDQUFlO0FBQ2hELElBQU0sWUFBWSxLQUFLLFFBQVEsVUFBVTtBQUV6QyxJQUFPLHNCQUFRLGFBQWE7QUFBQSxFQUMxQixTQUFTO0FBQUEsSUFDUCxJQUFJO0FBQUEsTUFDRixZQUFZO0FBQUEsUUFDVixZQUFZO0FBQUEsUUFDWixLQUFLO0FBQUEsVUFDSCxRQUFRO0FBQUEsWUFDTixRQUFRO0FBQUEsWUFDUixLQUFLO0FBQUEsVUFDUDtBQUFBLFVBQ0EsU0FBUztBQUFBLFVBQ1QsT0FBTztBQUFBLFlBQ0wsVUFBVSxDQUFDLE9BQU87QUFBQSxVQUNwQjtBQUFBLFVBQ0EsY0FBYztBQUFBLFlBQ1osU0FBUztBQUFBLGNBQ1A7QUFBQSxnQkFDRTtBQUFBLGdCQUNBO0FBQUEsa0JBQ0UsZ0JBQWdCO0FBQUEsb0JBQ2QsTUFBTSxDQUFDLGNBQWMsTUFBTTtBQUFBLGtCQUM3QjtBQUFBLGdCQUNGO0FBQUEsY0FDRjtBQUFBLFlBQ0Y7QUFBQSxVQUNGO0FBQUEsUUFDRjtBQUFBLE1BQ0Y7QUFBQSxJQUNGLENBQUM7QUFBQSxJQUNELElBQUk7QUFBQSxNQUNGLFNBQVMsQ0FBQyxtQkFBbUI7QUFBQSxNQUM3QixhQUFhO0FBQUEsSUFDZixDQUFDO0FBQUEsRUFDSDtBQUFBLEVBQ0EsT0FBTztBQUFBLElBQ0wsS0FBSztBQUFBLE1BQ0gsT0FBTyxRQUFRLFdBQVcsY0FBYztBQUFBLE1BQ3hDLFNBQVMsQ0FBQyxPQUFPLElBQUk7QUFBQSxNQUNyQixTQUFTLFFBQVE7QUFDZixlQUFPLFdBQVcsUUFBUSxhQUFhO0FBQUEsTUFDekM7QUFBQSxJQUNGO0FBQUEsSUFDQSxRQUFRLFFBQVEsV0FBVyxNQUFNO0FBQUEsRUFDbkM7QUFDRixDQUFDOyIsCiAgIm5hbWVzIjogW10KfQo=
|
||||
@@ -16,7 +16,7 @@
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsup-node src/index.ts",
|
||||
"build": "tsdown src/index.ts",
|
||||
"prepublishOnly": "npm run build",
|
||||
"watch": "npm run build -- --watch"
|
||||
},
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
"postbuild": "node scripts/postbuild.mjs",
|
||||
"prepublishOnly": "npm run build && npm run test",
|
||||
"postinstall": "patch-package",
|
||||
"build": "tsup-node src/browser.ts src/node.ts",
|
||||
"build": "tsdown src/browser.ts src/node.ts",
|
||||
"watch": "npm run build -- --watch"
|
||||
},
|
||||
"license": "GPL-3.0-or-later",
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsup-node src/index.ts",
|
||||
"build": "tsdown src/index.ts",
|
||||
"prepublishOnly": "npm run build",
|
||||
"watch": "npm run build -- --watch"
|
||||
},
|
||||
|
||||
@@ -20,6 +20,7 @@ import "isomorphic-fetch";
|
||||
import { mkdir, writeFile } from "fs/promises";
|
||||
import path from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
import { existsSync } from "fs";
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
@@ -36,6 +37,8 @@ async function main() {
|
||||
await mkdir(THEMES_DIRECTORY, { recursive: true });
|
||||
|
||||
for (const themeId of DEFAULT_THEMES) {
|
||||
const themePath = path.join(THEMES_DIRECTORY, `${themeId}.json`);
|
||||
if (existsSync(themePath)) continue;
|
||||
console.log("Getting", themeId);
|
||||
|
||||
const BASE_URL = `https://raw.githubusercontent.com/streetwriters/notesnook-themes/main/themes/${themeId}/v${THEME_COMPATIBILITY_VERSION}`;
|
||||
|
||||
@@ -46,8 +46,8 @@
|
||||
"react-dom": ">=18"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsup-node src/index.ts",
|
||||
"build": "tsdown src/index.ts",
|
||||
"prepublishOnly": "npm run build",
|
||||
"watch": "npm run build -- --watch"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,11 @@ const TSC =
|
||||
? path.join(__dirname, "..", "node_modules", ".bin", "tsc.cmd")
|
||||
: path.join(__dirname, "..", "node_modules", ".bin", "tsc");
|
||||
|
||||
const TSCGO =
|
||||
process.platform === "win32"
|
||||
? path.join(__dirname, "..", "node_modules", ".bin", "tsgo.cmd")
|
||||
: path.join(__dirname, "..", "node_modules", ".bin", "tsgo");
|
||||
|
||||
const esmPackageJson = {
|
||||
type: "module"
|
||||
};
|
||||
@@ -62,7 +67,7 @@ await Promise.all([
|
||||
IS_WATCH ? "--watch" : ""
|
||||
),
|
||||
cmd(
|
||||
TSC,
|
||||
TSCGO,
|
||||
"--emitDeclarationOnly",
|
||||
"--outDir",
|
||||
"dist/types",
|
||||
@@ -78,9 +83,10 @@ await Promise.all([
|
||||
)
|
||||
]);
|
||||
|
||||
function cmd(...command) {
|
||||
async function cmd(...command) {
|
||||
let p = spawn(command[0], command.slice(1), { shell: true });
|
||||
return new Promise((resolveFunc) => {
|
||||
console.time(command.join(" "));
|
||||
await new Promise((resolveFunc) => {
|
||||
p.stdout.on("data", (x) => {
|
||||
process.stdout.write(x.toString());
|
||||
});
|
||||
@@ -88,8 +94,9 @@ function cmd(...command) {
|
||||
process.stderr.write(x.toString());
|
||||
});
|
||||
p.on("exit", (code) => {
|
||||
console.log(command.join(" "), "exited with code", code);
|
||||
// console.log(command.join(" "), "exited with code", code);
|
||||
resolveFunc(code);
|
||||
});
|
||||
});
|
||||
console.timeEnd(command.join(" "));
|
||||
}
|
||||
|
||||
@@ -18,189 +18,352 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { spawn } from "child_process";
|
||||
import { fdir } from "fdir";
|
||||
import { existsSync, readFileSync, writeFileSync } from "fs";
|
||||
import { readFile } from "fs/promises";
|
||||
import { existsSync, readFileSync } from "fs";
|
||||
import { glob, readFile, rename, stat, writeFile } from "fs/promises";
|
||||
import path from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
import parser from "yargs-parser";
|
||||
import { performance } from "perf_hooks";
|
||||
import { createHash } from "crypto";
|
||||
|
||||
const args = parser(process.argv, {
|
||||
alias: { force: ["-f"], verbose: ["-v"] }
|
||||
});
|
||||
const isVerbose = process.env.CI || args.verbose;
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
const config = JSON.parse(
|
||||
readFileSync(path.join(__dirname, "..", "package.json"), "utf-8")
|
||||
).taskRunner;
|
||||
const cache = JSON.parse(
|
||||
existsSync(path.join(__dirname, "..", ".taskcache"))
|
||||
? readFileSync(path.join(__dirname, "..", ".taskcache"), "utf-8")
|
||||
: "{}"
|
||||
);
|
||||
|
||||
let [project, ...taskParts] = process.argv.slice(2)[0].split(":");
|
||||
const task = taskParts.join(":");
|
||||
|
||||
const allPackages = (
|
||||
await new fdir()
|
||||
.onlyDirs()
|
||||
.withMaxDepth(2)
|
||||
.glob(...config.projects)
|
||||
.crawl(".")
|
||||
.withPromise()
|
||||
).slice(4);
|
||||
|
||||
for (const pkg of allPackages) {
|
||||
if (isPackage(pkg, project)) {
|
||||
project = pkg;
|
||||
break;
|
||||
const args = { _: [], exclude: [], force: false, verbose: false, all: false };
|
||||
const shortFlags = { f: "force", v: "verbose", a: "all", e: "exclude" };
|
||||
for (let i = 2; i < process.argv.length; i++) {
|
||||
const arg = process.argv[i];
|
||||
if (!arg.startsWith("-")) args._.push(arg);
|
||||
else {
|
||||
const isLong = arg.startsWith("--");
|
||||
const [key, value] = isLong ? arg.split("=") : [arg[1], null];
|
||||
const flag = isLong ? key.slice(2) : shortFlags[key];
|
||||
if (!args[flag]) args._.push(arg);
|
||||
else if (Array.isArray(args[flag]))
|
||||
args[flag] = value
|
||||
? value.split(",")
|
||||
: process.argv[++i]?.split(",") || [];
|
||||
else if (flag) args[flag] = true;
|
||||
}
|
||||
}
|
||||
|
||||
const pipeline = await buildExecutionPipeline(
|
||||
config.taskDependencies[task],
|
||||
project
|
||||
);
|
||||
console.log("Found", pipeline.length, "dependencies to run.");
|
||||
for (const item of pipeline) {
|
||||
const checksum = await computeDirectoryChecksum(item.dep);
|
||||
if (checksum === cache[item.dep + ":" + item.cmd]) continue;
|
||||
|
||||
await runScript(item.cmd, item.dep);
|
||||
|
||||
cache[item.dep + ":" + item.cmd] = checksum;
|
||||
if (!args._.length) {
|
||||
console.error("Missing required argument: package:task");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
await runScript(task, project);
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const root = path.join(__dirname, "..");
|
||||
const isVerbose = !!(process.env.CI || args.verbose);
|
||||
const rootPkg = readJson(path.join(root, "package.json"));
|
||||
const config = rootPkg.taskRunner || { projects: ["packages/*"], tasks: [] };
|
||||
const cachePath = path.join(root, ".taskcache");
|
||||
const [project, ...taskParts] = args.all
|
||||
? [null, ...args._[0].split(":")]
|
||||
: args._[0].split(":");
|
||||
const cmd = taskParts.join(":");
|
||||
const pkgCache = new Map();
|
||||
const depMemo = new Map();
|
||||
|
||||
writeFileSync(
|
||||
path.join(__dirname, "..", ".taskcache"),
|
||||
JSON.stringify(cache, null, 2)
|
||||
);
|
||||
|
||||
async function runScript(command, pkg) {
|
||||
console.log(`Running "${command}" for ${pkg}`);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const child = spawn("npm", ["run", command], {
|
||||
cwd: pkg,
|
||||
stdio: isVerbose ? "inherit" : "pipe",
|
||||
shell: true
|
||||
});
|
||||
|
||||
child.on("exit", (code) => {
|
||||
if (code === 0) {
|
||||
resolve();
|
||||
} else {
|
||||
reject(new Error(`Command failed with exit code ${code}`));
|
||||
}
|
||||
});
|
||||
|
||||
child.on("error", (err) => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
const allPkgs = await findPkgs();
|
||||
const task = config.tasks?.find((t) => t.commands?.includes(cmd));
|
||||
if (!task) {
|
||||
console.error(`Task "${cmd}" not found in taskRunner config.`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
async function buildExecutionPipeline(commands, pkg) {
|
||||
if (commands.length === 0) return [];
|
||||
const resolvedPkgs = (
|
||||
await Promise.all(
|
||||
task.dependencies.flatMap((dep) =>
|
||||
allPkgs.map((pkg) => resolvePkg(dep, pkg))
|
||||
)
|
||||
)
|
||||
).filter(Boolean);
|
||||
|
||||
const executionPipeline = [];
|
||||
const deps = await findDependencies(pkg);
|
||||
for (const dep of deps) {
|
||||
if (executionPipeline.some((item) => item.dep === dep)) continue;
|
||||
|
||||
const pipeline = await buildExecutionPipeline(commands, dep);
|
||||
for (const item of pipeline) {
|
||||
if (executionPipeline.some((i) => i.dep === item.dep)) continue;
|
||||
executionPipeline.push(item);
|
||||
}
|
||||
|
||||
const json = JSON.parse(
|
||||
readFileSync(path.join(dep, "package.json"), "utf-8")
|
||||
);
|
||||
for (const cmd of commands) {
|
||||
if (json.scripts && json.scripts[cmd]) {
|
||||
executionPipeline.push({
|
||||
cmd,
|
||||
dep
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return executionPipeline;
|
||||
let cache = {};
|
||||
try {
|
||||
cache = existsSync(cachePath) ? readJson(cachePath) : {};
|
||||
} catch {
|
||||
if (isVerbose) console.warn("Failed to parse .taskcache");
|
||||
}
|
||||
|
||||
function isPackage(pkg, name) {
|
||||
const json = JSON.parse(
|
||||
readFileSync(path.join(pkg, "package.json"), "utf-8")
|
||||
);
|
||||
return json.name === name;
|
||||
}
|
||||
|
||||
async function findDependencies(scope) {
|
||||
function readPkg(pkgPath) {
|
||||
const key = path.resolve(pkgPath);
|
||||
if (pkgCache.has(key)) return pkgCache.get(key);
|
||||
try {
|
||||
const packageJsonPath = path.join(scope, "package.json");
|
||||
const packageJson = JSON.parse(await readFile(packageJsonPath, "utf-8"));
|
||||
const json = readJson(path.join(pkgPath, "package.json"));
|
||||
pkgCache.set(key, json);
|
||||
return json;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const dependencies = new Set([
|
||||
...filterDependencies(scope, packageJson.dependencies),
|
||||
...filterDependencies(scope, packageJson.devDependencies),
|
||||
...filterDependencies(scope, packageJson.optionalDependencies),
|
||||
...filterDependencies(scope, packageJson.peerDependencies)
|
||||
]);
|
||||
function readJson(jsonPath) {
|
||||
return JSON.parse(readFileSync(jsonPath, "utf-8"));
|
||||
}
|
||||
|
||||
for (const dependency of dependencies) {
|
||||
(await findDependencies(dependency)).forEach((v) => dependencies.add(v));
|
||||
}
|
||||
function getPkgName(pkgPath) {
|
||||
return readPkg(pkgPath)?.name || path.basename(pkgPath);
|
||||
}
|
||||
|
||||
return Array.from(dependencies.values());
|
||||
} catch (e) {
|
||||
console.error("Failed to find dependencies for", scope, "Error:", e);
|
||||
function isPkg(path, name) {
|
||||
const pkgName = getPkgName(path);
|
||||
return pkgName === name || pkgName.endsWith(`/${name}`);
|
||||
}
|
||||
|
||||
async function findPkgs() {
|
||||
const pkgs = [];
|
||||
for await (const entry of glob(config.projects)) {
|
||||
const pkgPath = path.join(root, entry);
|
||||
if (existsSync(path.join(pkgPath, "package.json")))
|
||||
pkgs.push(path.resolve(pkgPath));
|
||||
}
|
||||
return pkgs;
|
||||
}
|
||||
|
||||
function filterDeps(base, deps) {
|
||||
if (!deps) return [];
|
||||
let filteredDeps = [];
|
||||
for (const [key, value] of Object.entries(deps))
|
||||
if (key.startsWith("@notesnook/") && value?.startsWith("file:"))
|
||||
filteredDeps.push(path.resolve(base, value.slice(5)));
|
||||
return filteredDeps;
|
||||
}
|
||||
|
||||
async function findDeps(scope) {
|
||||
const key = path.resolve(scope);
|
||||
if (depMemo.has(key)) return depMemo.get(key);
|
||||
const pkg = readPkg(scope);
|
||||
if (!pkg) {
|
||||
depMemo.set(key, []);
|
||||
return [];
|
||||
}
|
||||
const deps = new Set(
|
||||
filterDeps(scope, { ...pkg.dependencies, ...pkg.devDependencies })
|
||||
);
|
||||
for (const d of deps) for (const c of await findDeps(d)) deps.add(c);
|
||||
|
||||
const result = Array.from(deps);
|
||||
depMemo.set(key, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
function filterDependencies(basePath, dependencies) {
|
||||
if (!dependencies) return [];
|
||||
return Object.entries(dependencies)
|
||||
.filter(
|
||||
([key, value]) =>
|
||||
key.startsWith("@notesnook/") || value.startsWith("file:")
|
||||
)
|
||||
.map(([_, value]) =>
|
||||
path.resolve(path.join(basePath, value.replace("file:", "")))
|
||||
);
|
||||
}
|
||||
|
||||
async function computeDirectoryChecksum(dir) {
|
||||
const exclusions = [
|
||||
"node_modules",
|
||||
".git",
|
||||
"dist",
|
||||
"build",
|
||||
"out",
|
||||
"locales",
|
||||
"coverage"
|
||||
];
|
||||
const crypto = await import("crypto");
|
||||
const hash = crypto.createHash("sha256");
|
||||
const files = (
|
||||
await new fdir()
|
||||
.withFullPaths()
|
||||
.exclude((dir) => exclusions.some((ex) => dir.includes(ex)))
|
||||
.crawl(path.join(dir))
|
||||
.withPromise()
|
||||
).sort();
|
||||
|
||||
for (const filePath of files) {
|
||||
const fileBuffer = await readFile(filePath);
|
||||
hash.update(fileBuffer);
|
||||
async function resolvePkg(command, pkgPath) {
|
||||
const json = readPkg(pkgPath);
|
||||
if (!json?.scripts?.[command]) return null;
|
||||
const deps = await findDeps(pkgPath);
|
||||
const taskDeps = {};
|
||||
for (const dep of deps) {
|
||||
if (readPkg(dep)?.scripts?.[command]) {
|
||||
taskDeps[command] = taskDeps[command] || [];
|
||||
taskDeps[command].push(dep);
|
||||
}
|
||||
}
|
||||
return { path: pkgPath, cmd: command, taskDependencies: taskDeps };
|
||||
}
|
||||
|
||||
async function runScript(command, pkg, opts) {
|
||||
opts = opts || {};
|
||||
const start = performance.now();
|
||||
const name = getPkgName(pkg);
|
||||
try {
|
||||
await new Promise((resolve, reject) => {
|
||||
const verbose = opts.verbose || isVerbose;
|
||||
const child = spawn("npm", ["run", command, ...(opts.args || [])], {
|
||||
cwd: pkg,
|
||||
stdio: verbose ? "inherit" : "pipe",
|
||||
shell: true
|
||||
});
|
||||
let output = "";
|
||||
if (!verbose && child.stdout) {
|
||||
child.stdout.on("data", (data) => (output += data));
|
||||
child.stderr.on("data", (data) => (output += data));
|
||||
}
|
||||
child.once("exit", (code) =>
|
||||
code === 0
|
||||
? resolve()
|
||||
: reject(new Error(`Exit code ${code}${output ? "\n" + output : ""}`))
|
||||
);
|
||||
child.once("error", reject);
|
||||
});
|
||||
console.log(
|
||||
`${name}:${command} took ${(performance.now() - start).toFixed(0)}ms`
|
||||
);
|
||||
} catch (err) {
|
||||
console.error(`${name}:${command} failed:`, err.message);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
function normalizePath(pathStr) {
|
||||
return path.resolve(pathStr).replace(/\/$/, "");
|
||||
}
|
||||
|
||||
async function toBatches(pkgs, cmd) {
|
||||
const batches = [pkgs.filter((pkg) => !pkg.taskDependencies[cmd]?.length)];
|
||||
const remaining = pkgs
|
||||
.filter((pkg) => !!pkg.taskDependencies[cmd]?.length)
|
||||
.slice();
|
||||
|
||||
while (remaining.length > 0) {
|
||||
const batch = [];
|
||||
for (const pkg of remaining.slice()) {
|
||||
if (
|
||||
pkg.taskDependencies[cmd].every((dep) =>
|
||||
batches.some((batch) =>
|
||||
batch.some(
|
||||
(batchPkg) => normalizePath(dep) === normalizePath(batchPkg.path)
|
||||
)
|
||||
)
|
||||
)
|
||||
) {
|
||||
batch.push(pkg);
|
||||
remaining.splice(remaining.indexOf(pkg), 1);
|
||||
}
|
||||
}
|
||||
if (!batch.length) throw new Error("Cyclic dependencies detected");
|
||||
batches.push(batch);
|
||||
}
|
||||
return batches;
|
||||
}
|
||||
|
||||
async function resolveRecursive(cmd, pkg, visited) {
|
||||
visited = visited || new Set();
|
||||
const result = [];
|
||||
const resolved = await resolvePkg(cmd, pkg);
|
||||
if (!resolved) return [];
|
||||
const rp = path.resolve(pkg);
|
||||
if (visited.has(rp)) return [];
|
||||
visited.add(rp);
|
||||
for (const dep of resolved.taskDependencies[cmd] || []) {
|
||||
for (const resolved of await resolveRecursive(cmd, dep, visited)) {
|
||||
if (!result.some((pkg) => pkg.path === resolved.path))
|
||||
result.push(resolved);
|
||||
}
|
||||
}
|
||||
result.push(resolved);
|
||||
return result;
|
||||
}
|
||||
|
||||
const exclusions = new Set([
|
||||
"node_modules",
|
||||
".git",
|
||||
"dist",
|
||||
"build",
|
||||
"out",
|
||||
"locales",
|
||||
"coverage",
|
||||
"__e2e__",
|
||||
"__mocks__",
|
||||
"__tests__",
|
||||
"__benches__",
|
||||
"scripts",
|
||||
"test-artifacts",
|
||||
"output"
|
||||
]);
|
||||
|
||||
async function computeChecksumFast(dir) {
|
||||
const hash = createHash("sha256");
|
||||
for await (const entry of glob(`${dir}/**/**`, {
|
||||
withFileTypes: true,
|
||||
exclude: (entry) => exclusions.has(entry.name)
|
||||
})) {
|
||||
if (entry.isFile()) {
|
||||
const fullPath = path.join(entry.parentPath, entry.name);
|
||||
const fileStat = await stat(fullPath).catch(() => null);
|
||||
if (!fileStat) continue;
|
||||
hash.update(fullPath);
|
||||
hash.update(fileStat.mtimeMs.toString());
|
||||
hash.update(fileStat.size.toString());
|
||||
}
|
||||
}
|
||||
return hash.digest("hex");
|
||||
}
|
||||
|
||||
async function computeChecksumSlow(dir) {
|
||||
const hash = createHash("sha256");
|
||||
const files = [];
|
||||
for await (const entry of glob(`${dir}/**/**`, {
|
||||
withFileTypes: true,
|
||||
exclude: (entry) => exclusions.has(entry.name)
|
||||
})) {
|
||||
if (entry.isFile()) files.push(path.join(entry.parentPath, entry.name));
|
||||
}
|
||||
files.sort();
|
||||
for (const file of files) {
|
||||
hash.update(path.relative(dir, file));
|
||||
hash.update(await readFile(file).catch(() => ""));
|
||||
}
|
||||
return hash.digest("hex");
|
||||
}
|
||||
|
||||
function findKeyInObject(obj, key) {
|
||||
if (!obj || typeof obj !== "object" || Array.isArray(obj)) return;
|
||||
if (key in obj) return obj[key];
|
||||
for (const value of Object.values(obj)) {
|
||||
const found = findKeyInObject(value, key);
|
||||
if (found) return found;
|
||||
}
|
||||
}
|
||||
|
||||
function getBuildFolder(pkgPath) {
|
||||
const pkg = readPkg(pkgPath);
|
||||
if (!pkg) return null;
|
||||
let artifact =
|
||||
pkg.main ||
|
||||
pkg.module ||
|
||||
pkg.types ||
|
||||
pkg.typings ||
|
||||
findKeyInObject(pkg.exports, "default") ||
|
||||
findKeyInObject(pkg.exports, "types");
|
||||
if (!artifact) return null;
|
||||
const dir = path.join(pkgPath, artifact.replace(/^\.\//, "").split("/")[0]);
|
||||
return existsSync(dir) ? dir : null;
|
||||
}
|
||||
|
||||
if (args.all) {
|
||||
console.time("Took");
|
||||
for (const dep of task.dependencies) {
|
||||
for (const batch of await toBatches(resolvedPkgs, dep)) {
|
||||
await Promise.all(
|
||||
batch
|
||||
.filter((pkg) => !args.exclude.some((name) => isPkg(pkg.path, name)))
|
||||
.map((task) => runScript(task.cmd, task.path))
|
||||
);
|
||||
}
|
||||
}
|
||||
console.timeEnd("Took");
|
||||
} else {
|
||||
console.time("Ready in");
|
||||
const pkg = resolvedPkgs.find((candidate) => isPkg(candidate.path, project));
|
||||
if (!pkg) {
|
||||
console.error(`Package "${project}" not found.`);
|
||||
process.exit(1);
|
||||
}
|
||||
for (const dep of task.dependencies) {
|
||||
for (const batch of await toBatches(
|
||||
await resolveRecursive(dep, pkg.path),
|
||||
dep
|
||||
)) {
|
||||
await Promise.all(
|
||||
batch.map(async (item) => {
|
||||
if (item.path === pkg.path) return;
|
||||
const hasBuild = !!getBuildFolder(item.path);
|
||||
const key = item.path + ":" + item.cmd;
|
||||
const [fast, slow] = cache[key]?.split("|") || [];
|
||||
const fastHash = await computeChecksumFast(item.path);
|
||||
if (hasBuild && !args.force && fast === fastHash) return;
|
||||
const slowHash = await computeChecksumSlow(item.path);
|
||||
if (hasBuild && !args.force && slow === slowHash) return;
|
||||
await runScript(item.cmd, item.path);
|
||||
cache[key] = `${fastHash}|${slowHash}`;
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
const tmp = cachePath + ".tmp";
|
||||
await writeFile(tmp, JSON.stringify(cache));
|
||||
await rename(tmp, cachePath);
|
||||
console.timeEnd("Ready in");
|
||||
await runScript(cmd, pkg.path, {
|
||||
verbose: true,
|
||||
args: ["--", ...args._.slice(1)]
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"target": "es2016",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "node",
|
||||
"incremental": true,
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
|
||||
@@ -17,12 +17,11 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { defineConfig } from "tsup";
|
||||
import { defineConfig } from "tsdown";
|
||||
|
||||
export default defineConfig({
|
||||
clean: true,
|
||||
cjsInterop: true,
|
||||
dts: true,
|
||||
splitting: false,
|
||||
format: ["cjs", "esm"]
|
||||
format: ["cjs", "esm"],
|
||||
dts: {
|
||||
tsgo: true
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user